diff --git a/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/CustomMustacheFactory.java b/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/CustomMustacheFactory.java index c8d28575fef47..773eed9fcb140 100644 --- a/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/CustomMustacheFactory.java +++ b/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/CustomMustacheFactory.java @@ -49,6 +49,7 @@ import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; +import java.io.Reader; import java.io.StringWriter; import java.io.Writer; import java.net.URLEncoder; @@ -104,6 +105,12 @@ public void encode(String value, Writer writer) { } } + /** Partial templates ({{>name}}) are not supported. */ + @Override + public Reader getReader(String resourceName) { + throw new MustacheException("Partial templates are not supported in OpenSearch Mustache scripts: [" + resourceName + "]"); + } + static Encoder createEncoder(String mimeType) { Supplier supplier = ENCODERS.get(mimeType); if (supplier == null) { diff --git a/modules/lang-mustache/src/test/java/org/opensearch/script/mustache/CustomMustacheFactoryTests.java b/modules/lang-mustache/src/test/java/org/opensearch/script/mustache/CustomMustacheFactoryTests.java index 422795ec4b171..95137c74ae437 100644 --- a/modules/lang-mustache/src/test/java/org/opensearch/script/mustache/CustomMustacheFactoryTests.java +++ b/modules/lang-mustache/src/test/java/org/opensearch/script/mustache/CustomMustacheFactoryTests.java @@ -34,6 +34,7 @@ import org.opensearch.script.Script; import org.opensearch.script.ScriptEngine; +import org.opensearch.script.ScriptException; import org.opensearch.script.TemplateScript; import org.opensearch.test.OpenSearchTestCase; @@ -44,6 +45,7 @@ import static org.opensearch.script.mustache.CustomMustacheFactory.JSON_MIME_TYPE; import static org.opensearch.script.mustache.CustomMustacheFactory.PLAIN_TEXT_MIME_TYPE; import static org.opensearch.script.mustache.CustomMustacheFactory.X_WWW_FORM_URLENCODED_MIME_TYPE; +import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; @@ -106,4 +108,29 @@ public void testUrlEncoder() { TemplateScript executable = compiled.newInstance(singletonMap("value", "tilde~ AND date:[2016 FROM*]")); assertThat(executable.execute(), equalTo("{\"field\": \"tilde%7E+AND+date%3A%5B2016+FROM*%5D\"}")); } + + public void testPartialTemplateWithFileUrlIsBlocked() { + final ScriptEngine engine = new MustacheScriptEngine(); + ScriptException e = expectThrows( + ScriptException.class, + () -> engine.compile(null, "{{>file:///tmp/secret.txt}}", TemplateScript.CONTEXT, emptyMap()) + ); + assertThat(e.getMessage(), containsString("Partial templates are not supported")); + } + + public void testPartialTemplateWithRelativePathIsBlocked() { + final ScriptEngine engine = new MustacheScriptEngine(); + ScriptException e = expectThrows( + ScriptException.class, + () -> engine.compile(null, "{{>../../../tmp/data}}", TemplateScript.CONTEXT, emptyMap()) + ); + assertThat(e.getMessage(), containsString("Partial templates are not supported")); + } + + public void testInlineTemplateWithoutPartialsStillRenders() { + final ScriptEngine engine = new MustacheScriptEngine(); + TemplateScript.Factory compiled = engine.compile(null, "hello {{name}}", TemplateScript.CONTEXT, emptyMap()); + TemplateScript executable = compiled.newInstance(singletonMap("name", "world")); + assertThat(executable.execute(), equalTo("hello world")); + } }