This is an automated email from the ASF dual-hosted git repository.
jacopoc pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ofbiz-framework.git
The following commit(s) were added to refs/heads/trunk by this push:
new e14e48c7e5 Fixed: Enhance sanitization of FreeMarker parameters to
check both strings and lists
e14e48c7e5 is described below
commit e14e48c7e51001422afff18773d9b1ce805fccc5
Author: Jacopo Cappellato <[email protected]>
AuthorDate: Wed Mar 11 08:32:45 2026 +0100
Fixed: Enhance sanitization of FreeMarker parameters to check both strings
and lists
---
.../apache/ofbiz/security/SecuredFreemarker.java | 45 +++++++++++++++++-----
1 file changed, 35 insertions(+), 10 deletions(-)
diff --git
a/framework/security/src/main/java/org/apache/ofbiz/security/SecuredFreemarker.java
b/framework/security/src/main/java/org/apache/ofbiz/security/SecuredFreemarker.java
index ef91dc9623..1a52887e37 100644
---
a/framework/security/src/main/java/org/apache/ofbiz/security/SecuredFreemarker.java
+++
b/framework/security/src/main/java/org/apache/ofbiz/security/SecuredFreemarker.java
@@ -106,26 +106,51 @@ public class SecuredFreemarker {
}
/**
- * Analyse each entry contains on params. If a freemarker template is
detected, sanatize it to escape any exploit
+ * Analyzes each entry in the parameter map and sanitizes any FreeMarker
interpolation expressions to prevent
+ * server-side template injection. Handles both single-value (String) and
multi-value (List<String>) parameters,
+ * since submitting the same parameter name multiple times causes OFBiz to
store it as a List, which would
+ * otherwise bypass a String-only check.
* @param params
* @return Map with all values sanitized
*/
public static Map<String, Object> sanitizeParameterMap(Map<String, Object>
params) {
List<Map.Entry<String, Object>> unsafeEntries =
params.entrySet().stream()
- .filter(entry -> entry.getValue() instanceof String
- && containsFreemarkerInterpolation((String)
entry.getValue()))
+ .filter(entry -> isUnsafeValue(entry.getValue()))
.toList();
if (!unsafeEntries.isEmpty()) {
Map<String, Object> paramsSanitize = new HashMap<>(params);
- unsafeEntries.forEach(entry -> {
- String sanitazedValue = (String) entry.getValue();
- for (String interpolation : FTL_INTERPOLATION) {
- sanitazedValue = sanitazedValue.replace(interpolation,
"##");
- }
- paramsSanitize.put(entry.getKey(), sanitazedValue);
- });
+ unsafeEntries.forEach(entry -> paramsSanitize.put(entry.getKey(),
sanitizeValue(entry.getValue())));
return paramsSanitize;
}
return params;
}
+
+ private static boolean isUnsafeValue(Object value) {
+ if (value instanceof String s) {
+ return containsFreemarkerInterpolation(s);
+ }
+ if (value instanceof List<?> list) {
+ return list.stream().anyMatch(item -> item instanceof String s &&
containsFreemarkerInterpolation(s));
+ }
+ return false;
+ }
+
+ private static Object sanitizeValue(Object value) {
+ if (value instanceof String s) {
+ return sanitizeString(s);
+ }
+ if (value instanceof List<?> list) {
+ return list.stream()
+ .map(item -> item instanceof String s ? sanitizeString(s)
: item)
+ .toList();
+ }
+ return value;
+ }
+
+ private static String sanitizeString(String value) {
+ for (String interpolation : FTL_INTERPOLATION) {
+ value = value.replace(interpolation, "##");
+ }
+ return value;
+ }
}