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

lukaszlenart pushed a commit to branch WW-4858-json-parameter-filtering
in repository https://gitbox.apache.org/repos/asf/struts.git

commit d2ebbedb218317b84e33b9de5b5b9752e16472f3
Author: Lukasz Lenart <[email protected]>
AuthorDate: Wed Jul 8 20:48:09 2026 +0200

    WW-4858 feat(json): opt-in applying excludeProperties/includeProperties to 
JSON input
    
    Co-Authored-By: Claude Opus 4.8 <[email protected]>
---
 .../org/apache/struts2/json/JSONInterceptor.java   | 35 ++++++++++++++++++++++
 .../apache/struts2/json/JSONInterceptorTest.java   | 35 ++++++++++++++++++++++
 2 files changed, 70 insertions(+)

diff --git 
a/plugins/json/src/main/java/org/apache/struts2/json/JSONInterceptor.java 
b/plugins/json/src/main/java/org/apache/struts2/json/JSONInterceptor.java
index a3d919e9c..0d584d830 100644
--- a/plugins/json/src/main/java/org/apache/struts2/json/JSONInterceptor.java
+++ b/plugins/json/src/main/java/org/apache/struts2/json/JSONInterceptor.java
@@ -89,6 +89,7 @@ public class JSONInterceptor extends AbstractInterceptor {
     private int paramNameMaxLength = 100;
     private Set<Pattern> excludedValuePatterns;
     private Set<Pattern> acceptedValuePatterns;
+    private boolean applyPropertyFiltersToInput = false;
 
     @SuppressWarnings("unchecked")
     public String intercept(ActionInvocation invocation) throws Exception {
@@ -291,6 +292,29 @@ public class JSONInterceptor extends AbstractInterceptor {
             LOG.debug("JSON body parameter [{}] rejected by ParameterNameAware 
action", fullPath);
             return false;
         }
+        if (applyPropertyFiltersToInput && 
!isAcceptedByPropertyFilters(fullPath)) {
+            LOG.debug("JSON body parameter [{}] rejected by 
excludeProperties/includeProperties on input", fullPath);
+            return false;
+        }
+        return true;
+    }
+
+    private boolean isAcceptedByPropertyFilters(String fullPath) {
+        if (excludeProperties != null) {
+            for (Pattern pattern : excludeProperties) {
+                if (pattern.matcher(fullPath).matches()) {
+                    return false;
+                }
+            }
+        }
+        if (includeProperties != null && !includeProperties.isEmpty()) {
+            for (Pattern pattern : includeProperties) {
+                if (pattern.matcher(fullPath).matches()) {
+                    return true;
+                }
+            }
+            return false;
+        }
         return true;
     }
 
@@ -767,6 +791,17 @@ public class JSONInterceptor extends AbstractInterceptor {
         this.acceptedValuePatterns = compileValuePatterns(commaDelim);
     }
 
+    /**
+     * When enabled, the interceptor's {@code excludeProperties}/{@code 
includeProperties} patterns —
+     * otherwise used only for serialization output — also gate which JSON 
keys are populated on input.
+     * Opt-in; defaults to {@code false} to preserve existing behavior.
+     *
+     * @param applyPropertyFiltersToInput true to apply property filters to 
deserialization
+     */
+    public void setApplyPropertyFiltersToInput(boolean 
applyPropertyFiltersToInput) {
+        this.applyPropertyFiltersToInput = applyPropertyFiltersToInput;
+    }
+
     private static Set<Pattern> compileValuePatterns(String commaDelim) {
         Set<String> raw = TextParseUtil.commaDelimitedStringToSet(commaDelim);
         Set<Pattern> compiled = new HashSet<>(raw.size());
diff --git 
a/plugins/json/src/test/java/org/apache/struts2/json/JSONInterceptorTest.java 
b/plugins/json/src/test/java/org/apache/struts2/json/JSONInterceptorTest.java
index bfa0abf0c..89ec91dfb 100644
--- 
a/plugins/json/src/test/java/org/apache/struts2/json/JSONInterceptorTest.java
+++ 
b/plugins/json/src/test/java/org/apache/struts2/json/JSONInterceptorTest.java
@@ -831,6 +831,41 @@ public class JSONInterceptorTest extends StrutsTestCase {
         assertNull(action.getBar());
     }
 
+    public void testExcludePropertiesAppliedToInputWhenEnabled() throws 
Exception {
+        this.request.setContent("{\"foo\":\"a\", \"bar\":\"b\"}".getBytes());
+        this.request.addHeader("Content-Type", "application/json");
+
+        JSONInterceptor interceptor = createInterceptor();
+        interceptor.setApplyPropertyFiltersToInput(true);
+        interceptor.setExcludeProperties("foo");
+        TestAction action = new TestAction();
+
+        this.invocation.setAction(action);
+        this.invocation.getStack().push(action);
+
+        interceptor.intercept(this.invocation);
+
+        assertNull(action.getFoo());
+        assertEquals("b", action.getBar());
+    }
+
+    public void testExcludePropertiesNotAppliedToInputByDefault() throws 
Exception {
+        this.request.setContent("{\"foo\":\"a\", \"bar\":\"b\"}".getBytes());
+        this.request.addHeader("Content-Type", "application/json");
+
+        JSONInterceptor interceptor = createInterceptor();
+        interceptor.setExcludeProperties("foo"); // configured, but flag off
+        TestAction action = new TestAction();
+
+        this.invocation.setAction(action);
+        this.invocation.getStack().push(action);
+
+        interceptor.intercept(this.invocation);
+
+        assertEquals("a", action.getFoo());
+        assertEquals("b", action.getBar());
+    }
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();

Reply via email to