This is an automated email from the ASF dual-hosted git repository. jacopoc pushed a commit to branch release24.09 in repository https://gitbox.apache.org/repos/asf/ofbiz-framework.git
commit fee350530775d07b9b4ff4c3a37a289affe33b8e 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 b0b788d0ec..3f4961a5ad 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; + } }

