diff --git a/template/fr.opensagres.xdocreport.template.freemarker/src/main/java/fr/opensagres/xdocreport/template/freemarker/FreemarkerTemplateEngine.java b/template/fr.opensagres.xdocreport.template.freemarker/src/main/java/fr/opensagres/xdocreport/template/freemarker/FreemarkerTemplateEngine.java index 51ecefd58..5ecec3a39 100644 --- a/template/fr.opensagres.xdocreport.template.freemarker/src/main/java/fr/opensagres/xdocreport/template/freemarker/FreemarkerTemplateEngine.java +++ b/template/fr.opensagres.xdocreport.template.freemarker/src/main/java/fr/opensagres/xdocreport/template/freemarker/FreemarkerTemplateEngine.java @@ -197,6 +197,21 @@ public void setFreemarkerConfiguration( Configuration freemarkerConfiguration ) { } this.freemarkerConfiguration.setLocalizedLookup( false ); + + // Security fix: Block dangerous class instantiation via ?new operator to prevent SSTI attacks + // + // This setting prevents Server-Side Template Injection (SSTI) attacks where malicious users + // By setting NEW_BUILTIN_CLASS_RESOLVER_KEY to "safer", FreeMarker will block instantiation + // of dangerous classes while still allowing legitimate template operations. + try + { + this.freemarkerConfiguration.setSetting( Configuration.NEW_BUILTIN_CLASS_RESOLVER_KEY, "safer" ); + } + catch ( Exception e ) + { + // Ignore configuration errors to maintain compatibility with older FreeMarker versions + // that might not support this security setting + } } public void extractFields( Reader reader, String entryName, FieldsExtractor extractor ) diff --git a/template/fr.opensagres.xdocreport.template.freemarker/src/test/java/fr/opensagres/xdocreport/template/freemarker/FreemarkerTemplateEngineSecurityTestCase.java b/template/fr.opensagres.xdocreport.template.freemarker/src/test/java/fr/opensagres/xdocreport/template/freemarker/FreemarkerTemplateEngineSecurityTestCase.java new file mode 100644 index 000000000..4ce1aa1cc --- /dev/null +++ b/template/fr.opensagres.xdocreport.template.freemarker/src/test/java/fr/opensagres/xdocreport/template/freemarker/FreemarkerTemplateEngineSecurityTestCase.java @@ -0,0 +1,67 @@ +package fr.opensagres.xdocreport.template.freemarker; + +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.Writer; + +import junit.framework.TestCase; +import fr.opensagres.xdocreport.core.XDocReportException; +import fr.opensagres.xdocreport.template.IContext; +import freemarker.template.Configuration; +import freemarker.template.TemplateException; + +/** + * Test case to verify security fixes for Server-Side Template Injection (SSTI) vulnerabilities + * in the FreeMarker template engine. + */ +public class FreemarkerTemplateEngineSecurityTestCase + extends TestCase +{ + + /** + * Test that legitimate template operations still work after security fix. + */ + public void testLegitimateTemplateOperationsStillWork() + throws Exception + { + FreemarkerTemplateEngine templateEngine = new FreemarkerTemplateEngine(); + + // Test basic variable substitution + String legitimateTemplate = "Hello ${name}!"; + Reader reader = new StringReader( legitimateTemplate ); + Writer writer = new StringWriter(); + IContext context = templateEngine.createContext(); + context.put( "name", "World" ); + + templateEngine.process( "", context, reader, writer ); + assertEquals( "Hello World!", writer.toString() ); + } + + + /** + * Test protection against SSTI payload with freemarker.template.utility.Execute. + */ + public void testSSTIProtectionAgainstExecutePayload() + throws Exception + { + FreemarkerTemplateEngine templateEngine = new FreemarkerTemplateEngine(); + + String maliciousTemplate ="${\"freemarker.template.utility.Execute\"?new()(\"whoami\")}"; + Reader reader = new StringReader( maliciousTemplate ); + Writer writer = new StringWriter(); + IContext context = templateEngine.createContext(); + + try + { + templateEngine.process( "", context, reader, writer ); + fail( "Security fix failed: Execute payload should be blocked" ); + } + catch ( XDocReportException e ) + { + assertTrue( "Expected security-related exception", + e.getCause() instanceof TemplateException ); + } + } +} +