diff --git a/README.md b/README.md
index 340b1e855..ade292403 100644
--- a/README.md
+++ b/README.md
@@ -6,6 +6,12 @@ includes integrated support for internationalization.
For more information please visit the [website](https://pebbletemplates.io).
+# Breaking changes in version 4.1.x
+
+- If you do not provide a custom Loader, Pebble will now use only a `ClasspathLoader` by default, same as the spring autoconfiguration.
+ Before that, it would have used an instance of the `DelegatingLoader` which consists of a `ClasspathLoader` and a `FileLoader` behind the scenes to find your templates.
+- Modify the `FileLoader` to use a mandatory sandboxed base directory parameter.
+
# Breaking changes in version 4.0.x
- Use one of the following artifactId according to the spring boot version that you are using
diff --git a/docs/pom.xml b/docs/pom.xml
index a29d31ab7..035100085 100644
--- a/docs/pom.xml
+++ b/docs/pom.xml
@@ -5,7 +5,7 @@
io.pebbletemplatespebble-project
- 4.0.1-SNAPSHOT
+ 4.1.0-SNAPSHOTdocs
diff --git a/docs/src/orchid/resources/changelog/v4_0_1.md b/docs/src/orchid/resources/changelog/v4_0_1.md
deleted file mode 100644
index 23d688929..000000000
--- a/docs/src/orchid/resources/changelog/v4_0_1.md
+++ /dev/null
@@ -1,8 +0,0 @@
----
-version: '4.0.1'
----
-
-- Use a default existing format of `yyyy-MM-dd'T'HH:mm:ssZ` when using date filter with a string (#677)
-- NaN mus return false instead of throwing an exception (#695)
-- Look for exact method / field match when doing reflection. Look for method get/is/has if none match
-- Update some dependencies (#709)
diff --git a/docs/src/orchid/resources/changelog/v4_1_0.md b/docs/src/orchid/resources/changelog/v4_1_0.md
new file mode 100644
index 000000000..4d168c1a2
--- /dev/null
+++ b/docs/src/orchid/resources/changelog/v4_1_0.md
@@ -0,0 +1,17 @@
+---
+version: '4.1.0'
+---
+
+# BREAKING CHANGES
+- Modify the `FileLoader` to use a mandatory sandboxed base directory parameter. (#715)
+- If you do not provide a custom Loader, Pebble will now use only a `ClasspathLoader` by default, same as the spring autoconfiguration. (#715)
+ Before that, it would have used an instance of the `DelegatingLoader` which consists of a `ClasspathLoader` and a `FileLoader` behind the scenes to find your templates.
+
+# New Features
+- Use a default existing format of `yyyy-MM-dd'T'HH:mm:ssZ` when using date filter with a string (#677)
+- Look for exact method / field match when doing reflection. Look for method get/is/has if none match
+- Update some dependencies (#709)
+
+# Bug Fixes
+- NaN must return false instead of throwing an exception (#695)
+- [CVE-2025-1686](https://nvd.nist.gov/vuln/detail/CVE-2025-1686).
diff --git a/docs/src/orchid/resources/wiki/guide/installation.md b/docs/src/orchid/resources/wiki/guide/installation.md
index ecfcd7b7b..3556702cf 100644
--- a/docs/src/orchid/resources/wiki/guide/installation.md
+++ b/docs/src/orchid/resources/wiki/guide/installation.md
@@ -61,18 +61,17 @@ finding your templates.
Pebble ships with the following loader implementations:
+- `DelegatingLoader`: Delegates responsibility to a collection of children loaders.
- `ClasspathLoader`: Uses a classloader to search the current classpath.
-- `FileLoader`: Finds templates using a filesystem path.
+- `FileLoader`: Finds templates using a filesystem path. Must provide a mandatory absolute base path.
- `ServletLoader`: Uses a servlet context to find the template. This is the recommended loader for use within an
application server but is not enabled by default.
- `Servlet5Loader`: Same as `ServletLoader`, but for Jakarta Servlet 5.0 or newer.
-- `StringLoader`: Considers the name of the template to be the contents of the template.
-- `DelegatingLoader`: Delegates responsibility to a collection of children loaders.
- `MemoryLoader`: Loader that supports inheritance and doesn't require a filesystem. This is useful for applications
+- `StringLoader`: Considers the name of the template to be the contents of the template. Should not be used in a production environment. It is primarily for testing and debugging. Many tags may not work when using this loader, such as "extends", "imports", etc.
that retrieve templates from a database for example.
-If you do not provide a custom Loader, Pebble will use an instance of the `DelegatingLoader` by default.
-This delegating loader will use a `ClasspathLoader` and a `FileLoader` behind the scenes to find your templates.
+If you do not provide a custom Loader, Pebble will use an instance of the `ClasspathLoader` by default.
## Pebble Engine Settings
@@ -85,7 +84,7 @@ All the settings are set during the construction of the `PebbleEngine` object.
| `tagCache` | An implementation of a ConcurrentMap cache that the Pebble engine will use for {{ anchor('cache tag', 'cache') }}. | Default implementation is `ConcurrentMapTagCache` and another implementation based on Caffeine is available (`CaffeineTagCache`) |
| `defaultLocale` | The default locale which will be passed to each compiled template. The templates then use this locale for functions such as i18n, etc. A template can also be given a unique locale during evaluation. | `Locale.getDefault()` |
| `executorService` | An `ExecutorService` that allows the usage of some advanced multithreading features, such as the `parallel` tag. | `null` |
-| `loader` | An implementation of the `Loader` interface which is used to find templates. | An implementation of the `DelegatingLoader` which uses a `ClasspathLoader` and a `FileLoader` behind the scenes. |
+| `loader` | An implementation of the `Loader` interface which is used to find templates. | An implementation of the `ClasspathLoader` |
| `strictVariables` | If set to true, Pebble will throw an exception if you try to access a variable or attribute that does not exist (or an attribute of a null variable). If set to false, your template will treat non-existing variables/attributes as null without ever skipping a beat. | `false` |
| `methodAccessValidator` | Pebble provides two implementations. NoOpMethodAccessValidator which do nothing and BlacklistMethodAccessValidator which checks that the method being called is not blacklisted. | `BlacklistMethodAccessValidator`
| `literalDecimalTreatedAsInteger` | option for treating literal decimals as `int`. Otherwise it is `long`. | `false` |
diff --git a/pebble-spring/pebble-legacy-spring-boot-starter/pom.xml b/pebble-spring/pebble-legacy-spring-boot-starter/pom.xml
index 6785ccbd9..ebf7be1a7 100644
--- a/pebble-spring/pebble-legacy-spring-boot-starter/pom.xml
+++ b/pebble-spring/pebble-legacy-spring-boot-starter/pom.xml
@@ -4,7 +4,7 @@
pebble-springio.pebbletemplates
- 4.0.1-SNAPSHOT
+ 4.1.0-SNAPSHOTpebble-legacy-spring-boot-starter
diff --git a/pebble-spring/pebble-spring-boot-starter/pom.xml b/pebble-spring/pebble-spring-boot-starter/pom.xml
index a6f3b5498..5ff60a036 100644
--- a/pebble-spring/pebble-spring-boot-starter/pom.xml
+++ b/pebble-spring/pebble-spring-boot-starter/pom.xml
@@ -4,7 +4,7 @@
pebble-springio.pebbletemplates
- 4.0.1-SNAPSHOT
+ 4.1.0-SNAPSHOTpebble-spring-boot-starter
diff --git a/pebble-spring/pebble-spring6/pom.xml b/pebble-spring/pebble-spring6/pom.xml
index ab0f8e8fa..60cae355b 100644
--- a/pebble-spring/pebble-spring6/pom.xml
+++ b/pebble-spring/pebble-spring6/pom.xml
@@ -4,7 +4,7 @@
pebble-springio.pebbletemplates
- 4.0.1-SNAPSHOT
+ 4.1.0-SNAPSHOTpebble-spring6
diff --git a/pebble-spring/pebble-spring7/pom.xml b/pebble-spring/pebble-spring7/pom.xml
index d16fbbe0b..afe535c94 100644
--- a/pebble-spring/pebble-spring7/pom.xml
+++ b/pebble-spring/pebble-spring7/pom.xml
@@ -4,7 +4,7 @@
pebble-springio.pebbletemplates
- 4.0.1-SNAPSHOT
+ 4.1.0-SNAPSHOTpebble-spring7
diff --git a/pebble-spring/pom.xml b/pebble-spring/pom.xml
index 26e24394c..7acdc37f0 100644
--- a/pebble-spring/pom.xml
+++ b/pebble-spring/pom.xml
@@ -4,7 +4,7 @@
io.pebbletemplatespebble-project
- 4.0.1-SNAPSHOT
+ 4.1.0-SNAPSHOTpebble-spring
diff --git a/pebble/pom.xml b/pebble/pom.xml
index 392653f32..2b0892902 100644
--- a/pebble/pom.xml
+++ b/pebble/pom.xml
@@ -3,7 +3,7 @@
io.pebbletemplatespebble-project
- 4.0.1-SNAPSHOT
+ 4.1.0-SNAPSHOTpebble
diff --git a/pebble/src/main/java/io/pebbletemplates/pebble/PebbleEngine.java b/pebble/src/main/java/io/pebbletemplates/pebble/PebbleEngine.java
index 648374a57..38a9296fc 100644
--- a/pebble/src/main/java/io/pebbletemplates/pebble/PebbleEngine.java
+++ b/pebble/src/main/java/io/pebbletemplates/pebble/PebbleEngine.java
@@ -9,6 +9,8 @@
package io.pebbletemplates.pebble;
+import io.pebbletemplates.pebble.attributes.methodaccess.BlacklistMethodAccessValidator;
+import io.pebbletemplates.pebble.attributes.methodaccess.MethodAccessValidator;
import io.pebbletemplates.pebble.cache.CacheKey;
import io.pebbletemplates.pebble.cache.PebbleCache;
import io.pebbletemplates.pebble.cache.tag.ConcurrentMapTagCache;
@@ -16,38 +18,31 @@
import io.pebbletemplates.pebble.cache.template.ConcurrentMapTemplateCache;
import io.pebbletemplates.pebble.cache.template.NoOpTemplateCache;
import io.pebbletemplates.pebble.error.LoaderException;
+import io.pebbletemplates.pebble.extension.*;
+import io.pebbletemplates.pebble.extension.escaper.EscapingStrategy;
import io.pebbletemplates.pebble.lexer.LexerImpl;
import io.pebbletemplates.pebble.lexer.Syntax;
import io.pebbletemplates.pebble.lexer.TokenStream;
+import io.pebbletemplates.pebble.loader.ClasspathLoader;
+import io.pebbletemplates.pebble.loader.Loader;
+import io.pebbletemplates.pebble.loader.StringLoader;
import io.pebbletemplates.pebble.node.RootNode;
import io.pebbletemplates.pebble.parser.Parser;
import io.pebbletemplates.pebble.parser.ParserImpl;
import io.pebbletemplates.pebble.parser.ParserOptions;
-import io.pebbletemplates.pebble.attributes.methodaccess.BlacklistMethodAccessValidator;
-import io.pebbletemplates.pebble.attributes.methodaccess.MethodAccessValidator;
-import io.pebbletemplates.pebble.extension.escaper.EscapingStrategy;
-import io.pebbletemplates.pebble.loader.ClasspathLoader;
-import io.pebbletemplates.pebble.loader.DelegatingLoader;
-import io.pebbletemplates.pebble.loader.FileLoader;
-import io.pebbletemplates.pebble.loader.Loader;
-import io.pebbletemplates.pebble.loader.StringLoader;
-import io.pebbletemplates.pebble.extension.*;
import io.pebbletemplates.pebble.template.EvaluationOptions;
import io.pebbletemplates.pebble.template.PebbleTemplate;
import io.pebbletemplates.pebble.template.PebbleTemplateImpl;
+import io.pebbletemplates.pebble.utils.TypeUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.Reader;
-import java.util.ArrayList;
-import java.util.List;
import java.util.Locale;
import java.util.concurrent.ExecutorService;
import java.util.function.Function;
-import io.pebbletemplates.pebble.utils.TypeUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
/**
* The main class used for compiling templates. The PebbleEngine is responsible for delegating
* responsibility to the lexer, parser, compiler, and template cache.
@@ -584,10 +579,7 @@ public PebbleEngine build() {
// default loader
if (this.loader == null) {
- List> defaultLoadingStrategies = new ArrayList<>();
- defaultLoadingStrategies.add(new ClasspathLoader());
- defaultLoadingStrategies.add(new FileLoader());
- this.loader = new DelegatingLoader(defaultLoadingStrategies);
+ this.loader = new ClasspathLoader();
}
// default locale
diff --git a/pebble/src/main/java/io/pebbletemplates/pebble/loader/FileLoader.java b/pebble/src/main/java/io/pebbletemplates/pebble/loader/FileLoader.java
index fd6dd0e61..9fb3924eb 100644
--- a/pebble/src/main/java/io/pebbletemplates/pebble/loader/FileLoader.java
+++ b/pebble/src/main/java/io/pebbletemplates/pebble/loader/FileLoader.java
@@ -10,18 +10,12 @@
import io.pebbletemplates.pebble.error.LoaderException;
import io.pebbletemplates.pebble.utils.PathUtils;
-
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.io.UnsupportedEncodingException;
+import java.io.*;
+import java.nio.file.Path;
+import java.nio.file.Paths;
/**
* This loader searches for a file located anywhere on the filesystem. It uses java.io.File to
@@ -34,69 +28,35 @@ public class FileLoader implements Loader {
private static final Logger logger = LoggerFactory.getLogger(FileLoader.class);
private String prefix;
-
private String suffix;
-
private String charset = "UTF-8";
+ public FileLoader(String prefix) {
+ this.setPrefix(prefix);
+ }
+
@Override
public Reader getReader(String templateName) {
- // try to load File
- InputStream is = null;
File file = this.getFile(templateName);
- if (file.exists() && file.isFile()) {
- try {
- is = new FileInputStream(file);
- } catch (FileNotFoundException e) {
- }
- }
-
- if (is == null) {
- throw new LoaderException(null,
- "Could not find template \"" + templateName + "\"");
- }
-
try {
+ InputStream is = new FileInputStream(file);
return new BufferedReader(new InputStreamReader(is, this.charset));
+ } catch (FileNotFoundException e) {
+ throw new LoaderException(e, String.format("Could not find template [prefix='%s', templateName='%s']", this.prefix, templateName));
} catch (UnsupportedEncodingException e) {
+ throw new LoaderException(e, String.format("Invalid charset '%s'", this.charset));
}
-
- return null;
}
private File getFile(String templateName) {
- // add the prefix and ensure the prefix ends with a separator character
- StringBuilder path = new StringBuilder();
- if (this.getPrefix() != null) {
-
- path.append(this.getPrefix());
-
- if (!this.getPrefix().endsWith(String.valueOf(File.separatorChar))) {
- path.append(File.separatorChar);
- }
- }
-
templateName = templateName + (this.getSuffix() == null ? "" : this.getSuffix());
+ templateName = PathUtils.sanitize(templateName, File.separatorChar);
- logger.trace("Looking for template in {}{}.", path.toString(), templateName);
+ Path path = Paths.get(this.getPrefix(), templateName);
+ logger.trace("Looking for template in {}.", path);
- /*
- * if template name contains path segments, move those segments into the
- * path variable. The below technique needs to know the difference
- * between the path and file name.
- */
- String[] pathSegments = PathUtils.PATH_SEPARATOR_REGEX.split(templateName);
-
- if (pathSegments.length > 1) {
- // file name is the last segment
- templateName = pathSegments[pathSegments.length - 1];
- }
- for (int i = 0; i < (pathSegments.length - 1); i++) {
- path.append(pathSegments[i]).append(File.separatorChar);
- }
-
- // try to load File
- return new File(path.toString(), templateName);
+ this.checkIfDirectoryTraversal(templateName);
+ return path.toFile();
}
public String getSuffix() {
@@ -114,7 +74,17 @@ public String getPrefix() {
@Override
public void setPrefix(String prefix) {
- this.prefix = prefix;
+ if (prefix == null) {
+ throw new LoaderException(null, "Prefix cannot be null");
+ }
+ String trimmedPrefix = prefix.trim();
+ if (trimmedPrefix.isEmpty()) {
+ throw new LoaderException(null, "Prefix cannot be empty");
+ }
+ if (!Paths.get(trimmedPrefix).isAbsolute()) {
+ throw new LoaderException(null, "Prefix must be an absolute path");
+ }
+ this.prefix = trimmedPrefix;
}
public String getCharset() {
@@ -140,4 +110,23 @@ public String createCacheKey(String templateName) {
public boolean resourceExists(String templateName) {
return this.getFile(templateName).exists();
}
+
+ private void checkIfDirectoryTraversal(String templateName) {
+ Path baseDirPath = Paths.get(prefix);
+ Path userPath = Paths.get(templateName);
+ if (userPath.isAbsolute()) {
+ throw new LoaderException(null, String.format("templateName '%s' must be relative", templateName));
+ }
+
+ // Join the two paths together, then normalize so that any ".." elements
+ // in the userPath can remove parts of baseDirPath.
+ // (e.g. "/foo/bar/baz" + "../attack" -> "/foo/bar/attack")
+ Path resolvedPath = baseDirPath.resolve(userPath).normalize();
+
+ // Make sure the resulting path is still within the required directory.
+ // (In the example above, "/foo/bar/attack" is not.)
+ if (!resolvedPath.startsWith(baseDirPath)) {
+ throw new LoaderException(null, String.format("template is not in the base directory path [baseDir='%s', templateName='%s']", this.prefix, templateName));
+ }
+ }
}
diff --git a/pebble/src/main/java/io/pebbletemplates/pebble/utils/PathUtils.java b/pebble/src/main/java/io/pebbletemplates/pebble/utils/PathUtils.java
index 6f9f482f8..e5241c87a 100644
--- a/pebble/src/main/java/io/pebbletemplates/pebble/utils/PathUtils.java
+++ b/pebble/src/main/java/io/pebbletemplates/pebble/utils/PathUtils.java
@@ -44,7 +44,7 @@ public static String resolveRelativePath(String relativePath, String anchorPath,
return null;
}
- private static String sanitize(String path, char expectedSeparator) {
+ public static String sanitize(String path, char expectedSeparator) {
return PATH_SEPARATOR_REGEX.matcher(path)
.replaceAll(Matcher.quoteReplacement(String.valueOf(expectedSeparator)));
}
diff --git a/pebble/src/test/java/io/pebbletemplates/pebble/FileLoaderTest.java b/pebble/src/test/java/io/pebbletemplates/pebble/FileLoaderTest.java
new file mode 100644
index 000000000..aa958f58c
--- /dev/null
+++ b/pebble/src/test/java/io/pebbletemplates/pebble/FileLoaderTest.java
@@ -0,0 +1,132 @@
+package io.pebbletemplates.pebble;
+
+import io.pebbletemplates.pebble.error.LoaderException;
+import io.pebbletemplates.pebble.error.PebbleException;
+import io.pebbletemplates.pebble.loader.FileLoader;
+import io.pebbletemplates.pebble.loader.Loader;
+import io.pebbletemplates.pebble.template.PebbleTemplate;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.net.URISyntaxException;
+import java.nio.file.Paths;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class FileLoaderTest {
+
+ @Test
+ void testFileLoaderPrefixNull() {
+ assertThrows(LoaderException.class, () -> new FileLoader(null));
+ }
+
+ @Test
+ void testFileLoaderPrefixEmpty() {
+ assertThrows(LoaderException.class, () -> new FileLoader(" "));
+ }
+
+ @Test
+ void testFileLoaderPrefixRelativePath() {
+ assertThrows(LoaderException.class, () -> new FileLoader(" ../bar "));
+ }
+
+ @Test
+ void testFileLoader() throws PebbleException, IOException, URISyntaxException {
+ String prefix = Paths.get(this.getClass().getClassLoader().getResource("templates").toURI()).toString();
+ Loader> loader = new FileLoader(prefix);
+ loader.setSuffix(".suffix");
+ PebbleEngine engine = new PebbleEngine.Builder().loader(loader).strictVariables(false).build();
+ PebbleTemplate template1 = engine.getTemplate("template.loaderTest.peb");
+ Writer writer1 = new StringWriter();
+ template1.evaluate(writer1);
+ assertEquals("SUCCESS", writer1.toString());
+ }
+
+ @Test
+ void testFileLoaderAbsoluteTemplateName() throws PebbleException, URISyntaxException {
+ String prefix = Paths.get(this.getClass().getClassLoader().getResource("templates").toURI()).toString();
+ Loader> loader = new FileLoader(prefix);
+ loader.setSuffix(".suffix");
+ PebbleEngine engine = new PebbleEngine.Builder().loader(loader).strictVariables(false).build();
+ assertThrows(LoaderException.class, () -> engine.getTemplate("/template.loaderTest.peb"));
+ }
+
+ @Test
+ void testFileLoaderTemplateNameIsADirectory() throws PebbleException, URISyntaxException {
+ String prefix = Paths.get(this.getClass().getClassLoader().getResource("templates").toURI()).toString();
+ Loader> loader = new FileLoader(prefix);
+ PebbleEngine engine = new PebbleEngine.Builder().loader(loader).strictVariables(false).build();
+ assertThrows(LoaderException.class, () -> engine.getTemplate("loader"));
+ }
+
+ @Test
+ void testFileLoaderRelativeTemplateName() throws PebbleException, IOException, URISyntaxException {
+ String prefix = Paths.get(this.getClass().getClassLoader().getResource("templates").toURI()).getParent().toString();
+ Loader> loader = new FileLoader(prefix);
+ loader.setSuffix(".suffix");
+ PebbleEngine engine = new PebbleEngine.Builder().loader(loader).strictVariables(false).build();
+ PebbleTemplate template1 = engine.getTemplate("templates/template.loaderTest.peb");
+ Writer writer1 = new StringWriter();
+ template1.evaluate(writer1);
+ assertEquals("SUCCESS", writer1.toString());
+ }
+
+ @Test
+ void testFileLoaderPathTraversal() throws PebbleException, URISyntaxException {
+ String prefix = Paths.get(this.getClass().getClassLoader().getResource("templates").toURI()).toString();
+ Loader> loader = new FileLoader(prefix);
+ loader.setSuffix(".peb");
+ PebbleEngine engine = new PebbleEngine.Builder().loader(loader).strictVariables(false).build();
+ assertThrows(LoaderException.class, () -> engine.getTemplate("../template-tests/DoubleNestedIfStatement"));
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {"%2e%2e%2f", "%2e%2e/", "..%2f", "%2e%2e%5c", "%2e%2e\\", "..%5c", "%252e%252e%255c", "..%255c"})
+ void testFileLoaderPathTraversalEncoded(String relativePath) throws URISyntaxException {
+ String prefix = Paths.get(this.getClass().getClassLoader().getResource("templates").toURI()).toString();
+ Loader> loader = new FileLoader(prefix);
+ loader.setSuffix(".peb");
+ PebbleEngine engine = new PebbleEngine.Builder().loader(loader).strictVariables(false).build();
+ assertThrows(LoaderException.class, () -> engine.getTemplate(relativePath + "template-tests/DoubleNestedIfStatement"));
+ }
+
+ @Test
+ void testFileLoaderUnsupportedCharset() throws PebbleException, URISyntaxException {
+ String prefix = Paths.get(this.getClass().getClassLoader().getResource("templates").toURI()).toString();
+ Loader> loader = new FileLoader(prefix);
+ loader.setCharset("foobar");
+ PebbleEngine engine = new PebbleEngine.Builder().loader(loader).strictVariables(false).build();
+ assertThrows(LoaderException.class, () -> engine.getTemplate("template.loaderTest.peb"));
+ }
+
+ /**
+ * Tests if relative includes work. Issue #162.
+ */
+ @Test
+ void testFileLoaderPathWithBackslash() throws IOException, URISyntaxException {
+ String prefix = Paths.get(this.getClass().getClassLoader().getResource("templates").toURI()).toString();
+ PebbleEngine pebble = new PebbleEngine.Builder().loader(new FileLoader(prefix)).build();
+ PebbleTemplate template = pebble.getTemplate("relativepath/subdirectory1/template.forwardslashes.peb".replace("/", "\\")); // ensure backslashes in all environments
+ Writer writer = new StringWriter();
+ template.evaluate(writer);
+ assertEquals("included", writer.toString());
+ }
+
+ /**
+ * Issue #162.
+ */
+ @Test
+ void testFileLoaderPathWithForwardSlash() throws IOException, URISyntaxException {
+ String prefix = Paths.get(this.getClass().getClassLoader().getResource("templates").toURI()).toString();
+ PebbleEngine pebble = new PebbleEngine.Builder().loader(new FileLoader(prefix)).build();
+ PebbleTemplate template = pebble.getTemplate("relativepath/subdirectory1/template.backwardslashes.peb");
+ Writer writer = new StringWriter();
+ template.evaluate(writer);
+ assertEquals("included", writer.toString());
+ }
+}
diff --git a/pebble/src/test/java/io/pebbletemplates/pebble/LoaderTest.java b/pebble/src/test/java/io/pebbletemplates/pebble/LoaderTest.java
index 7394f4bef..4c632e0c7 100644
--- a/pebble/src/test/java/io/pebbletemplates/pebble/LoaderTest.java
+++ b/pebble/src/test/java/io/pebbletemplates/pebble/LoaderTest.java
@@ -15,7 +15,6 @@
import org.junit.jupiter.api.Test;
import java.io.*;
-import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
@@ -69,19 +68,6 @@ void testClassLoaderLoaderWithNestedTemplateInJar() throws PebbleException, IOEx
}
- @Test
- void testFileLoader() throws PebbleException, IOException, URISyntaxException {
- Loader> loader = new FileLoader();
- loader.setSuffix(".suffix");
- PebbleEngine engine = new PebbleEngine.Builder().loader(loader).strictVariables(false).build();
- URL url = this.getClass().getResource("/templates/template.loaderTest.peb");
- PebbleTemplate template1 = engine.getTemplate(new File(url.toURI()).getPath());
- Writer writer1 = new StringWriter();
- template1.evaluate(writer1);
- assertEquals("SUCCESS", writer1.toString());
-
- }
-
@Test
void testDelegatingLoader() throws PebbleException, IOException {
List> loaders = new ArrayList<>();
diff --git a/pebble/src/test/java/io/pebbletemplates/pebble/TestRelativePath.java b/pebble/src/test/java/io/pebbletemplates/pebble/TestRelativePath.java
index 5eabda4dc..6f9bc42e9 100644
--- a/pebble/src/test/java/io/pebbletemplates/pebble/TestRelativePath.java
+++ b/pebble/src/test/java/io/pebbletemplates/pebble/TestRelativePath.java
@@ -1,17 +1,12 @@
package io.pebbletemplates.pebble;
import io.pebbletemplates.pebble.error.PebbleException;
-import io.pebbletemplates.pebble.loader.FileLoader;
import io.pebbletemplates.pebble.template.PebbleTemplate;
-
import org.junit.jupiter.api.Test;
-import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
-import java.net.URISyntaxException;
-import java.net.URL;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -65,38 +60,4 @@ void testRelativeImports() throws PebbleException, IOException {
assertEquals("",
writer.toString().replaceAll("\\r?\\n", "").replace("\t", ""));
}
-
- /**
- * Tests if relative includes work. Issue #162.
- */
- @Test
- void testPathWithBackslashesWithRelativePathWithForwardSlashes()
- throws PebbleException, IOException, URISyntaxException {
- PebbleEngine pebble = new PebbleEngine.Builder().loader(new FileLoader()).build();
- URL url = this.getClass()
- .getResource("/templates/relativepath/subdirectory1/template.forwardslashes.peb");
- PebbleTemplate template = pebble
- .getTemplate(new File(url.toURI()).getPath()
- .replace("/", "\\")); // ensure backslashes in all environments
- Writer writer = new StringWriter();
- template.evaluate(writer);
- assertEquals("included", writer.toString());
- }
-
- /**
- * Issue #162.
- */
- @Test
- void testPathWithForwardSlashesWithRelativePathWithBackwardSlashes()
- throws PebbleException, IOException, URISyntaxException {
- PebbleEngine pebble = new PebbleEngine.Builder().loader(new FileLoader()).build();
- URL url = this.getClass()
- .getResource("/templates/relativepath/subdirectory1/template.backwardslashes.peb");
- PebbleTemplate template = pebble
- .getTemplate(new File(url.toURI()).getPath()
- .replace("\\", "/")); // ensure forward slashes in all environments
- Writer writer = new StringWriter();
- template.evaluate(writer);
- assertEquals("included", writer.toString());
- }
}
diff --git a/pom.xml b/pom.xml
index d67b99f93..d19647073 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
4.0.0io.pebbletemplatespebble-project
- 4.0.1-SNAPSHOT
+ 4.1.0-SNAPSHOTpom