This is an automated email from the ASF dual-hosted git repository.

lukaszlenart pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/struts.git


The following commit(s) were added to refs/heads/main by this push:
     new 3007624d3 WW-5829 fix(interceptor): apply parameter value filters to 
every value of a multi-valued parameter (#2087)
3007624d3 is described below

commit 3007624d339c9d223163f4504873c34eb542ee6f
Author: g0w6y <[email protected]>
AuthorDate: Sun Nov 8 20:27:13 2026 +0530

    WW-5829 fix(interceptor): apply parameter value filters to every value of a 
multi-valued parameter (#2087)
    
    * WW-5829 fix(interceptor): apply parameter value filters to every value of 
a multi-valued parameter
    
    ParametersInterceptor passed only Parameter.getValue() (the first value) to
    ParameterValueAware and the excluded/accepted value patterns, so a request 
such
    as a=ok&a=evil was accepted although "evil" would have been rejected. Check 
each
    value. Unlike JSONInterceptor, which drops only the bad list element, a 
single
    rejected value rejects the whole parameter (as a rejected first value 
already
    did). Single-valued and empty parameters behave as before.
    
    * WW-5829 test: assert bound values, add missing firstBad case, widen 
valuesToCheck
    
    Close the two required test gaps from review:
    
    - testAcceptedValuePatternsApplyToEveryValueOfMultiValuedParameter had no
      firstBad case, unlike the other two tests, so a version that skipped
      index 0 wouldn't have been caught here.
    - All three multi-value tests asserted only actual.keySet(), so a version
      that accepted a parameter but bound only its first value would still
      have passed. Add assertArrayEquals on the bound value in each.
    
    Also widen valuesToCheck from private to protected, so a subclass
    overriding isAcceptableParameterValue/isAcceptableParameterValueAware can
    reuse the same expansion instead of falling back to param.getValue().
---
 .../parameter/ParametersInterceptor.java           | 10 +++-
 .../parameter/ParametersInterceptorTest.java       | 61 ++++++++++++++++++++++
 2 files changed, 69 insertions(+), 2 deletions(-)

diff --git 
a/core/src/main/java/org/apache/struts2/interceptor/parameter/ParametersInterceptor.java
 
b/core/src/main/java/org/apache/struts2/interceptor/parameter/ParametersInterceptor.java
index 254e471cc..38ac592f3 100644
--- 
a/core/src/main/java/org/apache/struts2/interceptor/parameter/ParametersInterceptor.java
+++ 
b/core/src/main/java/org/apache/struts2/interceptor/parameter/ParametersInterceptor.java
@@ -549,11 +549,17 @@ public class ParametersInterceptor extends 
MethodFilterInterceptor {
      * @return true if parameter is accepted
      */
     protected boolean isAcceptableParameterValue(Parameter param, Object 
action) {
-        return isAcceptableParameterValueAware(param, action) && 
isAcceptableValue(param.getName(), param.getValue());
+        return isAcceptableParameterValueAware(param, action)
+                && Arrays.stream(valuesToCheck(param)).allMatch(value -> 
isAcceptableValue(param.getName(), value));
     }
 
     protected boolean isAcceptableParameterValueAware(Parameter param, Object 
action) {
-        return !(action instanceof ParameterValueAware valueAware) || 
valueAware.acceptableParameterValue(param.getValue());
+        return !(action instanceof ParameterValueAware valueAware)
+                || 
Arrays.stream(valuesToCheck(param)).allMatch(valueAware::acceptableParameterValue);
+    }
+
+    protected String[] valuesToCheck(Parameter param) {
+        return param.isMultiple() ? param.getMultipleValues() : new 
String[]{param.getValue()};
     }
 
     /**
diff --git 
a/core/src/test/java/org/apache/struts2/interceptor/parameter/ParametersInterceptorTest.java
 
b/core/src/test/java/org/apache/struts2/interceptor/parameter/ParametersInterceptorTest.java
index 2e1301071..ffe934551 100644
--- 
a/core/src/test/java/org/apache/struts2/interceptor/parameter/ParametersInterceptorTest.java
+++ 
b/core/src/test/java/org/apache/struts2/interceptor/parameter/ParametersInterceptorTest.java
@@ -18,6 +18,8 @@
  */
 package org.apache.struts2.interceptor.parameter;
 
+import static org.junit.Assert.assertArrayEquals;
+
 import org.apache.struts2.action.Action;
 import org.apache.struts2.ActionContext;
 import org.apache.struts2.ActionProxy;
@@ -65,6 +67,7 @@ import java.util.LinkedHashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 
 /**
@@ -1133,6 +1136,64 @@ public class ParametersInterceptorTest extends 
XWorkTestCase {
         assertEquals(expected, actual);
     }
 
+    public void 
testExcludedValuePatternsApplyToEveryValueOfMultiValuedParameter() throws 
Exception {
+        ParametersInterceptor pi = createParametersInterceptor();
+        pi.setExcludedValuePatterns(".*\\$\\{.*?\\}.*");
+
+        final Map<String, Object> actual = injectValueStackFactory(pi);
+        ValueStack stack = injectValueStack(actual);
+
+        Map<String, Object> parameters = new HashMap<>();
+        parameters.put("single", new String[]{"fooValue"});
+        parameters.put("allGood", new String[]{"fooValue", "barValue"});
+        parameters.put("firstBad", new String[]{"${2+2}", "fooValue"});
+        parameters.put("secondBad", new String[]{"fooValue", "${2+2}"});
+        pi.applyParameters(new NoParametersAction(), stack, 
HttpParameters.create(parameters).build());
+
+        assertEquals(Set.of("single", "allGood"), actual.keySet());
+        assertArrayEquals(new String[]{"fooValue"}, (String[]) 
actual.get("single"));
+        assertArrayEquals(new String[]{"fooValue", "barValue"}, (String[]) 
actual.get("allGood"));
+    }
+
+    public void 
testAcceptedValuePatternsApplyToEveryValueOfMultiValuedParameter() throws 
Exception {
+        ParametersInterceptor pi = createParametersInterceptor();
+        pi.setAcceptedValuePatterns("^fooValue.*");
+
+        final Map<String, Object> actual = injectValueStackFactory(pi);
+        ValueStack stack = injectValueStack(actual);
+
+        Map<String, Object> parameters = new HashMap<>();
+        parameters.put("single", new String[]{"fooValue"});
+        parameters.put("allGood", new String[]{"fooValue", "fooValue2"});
+        parameters.put("firstBad", new String[]{"barValue", "fooValue"});
+        parameters.put("secondBad", new String[]{"fooValue", "barValue"});
+        pi.applyParameters(new NoParametersAction(), stack, 
HttpParameters.create(parameters).build());
+
+        assertEquals(Set.of("single", "allGood"), actual.keySet());
+        assertArrayEquals(new String[]{"fooValue"}, (String[]) 
actual.get("single"));
+        assertArrayEquals(new String[]{"fooValue", "fooValue2"}, (String[]) 
actual.get("allGood"));
+    }
+
+    public void 
testParameterValueAwareIsAskedAboutEveryValueOfMultiValuedParameter() throws 
Exception {
+        ParametersInterceptor pi = createParametersInterceptor();
+
+        final Map<String, Object> actual = injectValueStackFactory(pi);
+        ValueStack stack = injectValueStack(actual);
+
+        Object a = (ParameterValueAware) parameterValue -> 
!"evil".equals(parameterValue);
+
+        Map<String, Object> parameters = new HashMap<>();
+        parameters.put("single", new String[]{"fooValue"});
+        parameters.put("allGood", new String[]{"fooValue", "barValue"});
+        parameters.put("firstBad", new String[]{"evil", "fooValue"});
+        parameters.put("secondBad", new String[]{"fooValue", "evil"});
+        pi.applyParameters(a, stack, 
HttpParameters.create(parameters).build());
+
+        assertEquals(Set.of("single", "allGood"), actual.keySet());
+        assertArrayEquals(new String[]{"fooValue"}, (String[]) 
actual.get("single"));
+        assertArrayEquals(new String[]{"fooValue", "barValue"}, (String[]) 
actual.get("allGood"));
+    }
+
 
     private ValueStack injectValueStack(Map<String, Object> actual) {
         ValueStack stack = createStubValueStack(actual);

Reply via email to