Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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" );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this constant exists in any freemarker version? I - think you should catch Exception instead of catching TemplateException

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@angelozerr Thanks for the question!
Configuration.NEW_BUILTIN_CLASS_RESOLVER_KEY was introduced in FreeMarker 2.3.30, so it is supported in the FreeMarker version currently used in XDocReport (2.3.32).

}
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 )
Expand Down
Original file line number Diff line number Diff line change
@@ -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 );
}
}
}