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);