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-site.git
The following commit(s) were added to refs/heads/main by this push:
new 08e3ed4e3 docs: scope the @StrutsParameter "never set" claim to named
properties (#328)
08e3ed4e3 is described below
commit 08e3ed4e3de735c77840d0e2d197ae7a6a2351c5
Author: Lukasz Lenart <[email protected]>
AuthorDate: Fri Sep 11 06:00:11 2026 +0200
docs: scope the @StrutsParameter "never set" claim to named properties
(#328)
The annotation page told readers that JSON and REST deserialization means
unauthorized fields are never set on the target object. That is true of the
properties the deserializer binds by name, but a Jackson any-setter is a
separate sink that the REST plugin's authorization wrapper never wraps, so
unknown keys routed to it are bound with no @StrutsParameter check at all --
in the same request in which an ordinary unannotated setter on the same
class
is correctly rejected.
Qualify the bullet and document the gap in its own section, alongside the
existing creator-bound-properties note that covers the same class of
problem.
The JSON plugin is unaffected: it does not use Jackson, so its own page's
identical wording stays accurate and is left alone.
Claude-Session: https://claude.ai/code/session_012HF8BGrYmCVnqUdQJJ1XPM
Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
---
.../core-developers/struts-parameter-annotation.md | 26 +++++++++++++++++++++-
1 file changed, 25 insertions(+), 1 deletion(-)
diff --git a/source/core-developers/struts-parameter-annotation.md
b/source/core-developers/struts-parameter-annotation.md
index 916607c7b..c9a1642c2 100644
--- a/source/core-developers/struts-parameter-annotation.md
+++ b/source/core-developers/struts-parameter-annotation.md
@@ -27,7 +27,10 @@ channel that can populate an action from request data:
action chaining (opt-in via `struts.chaining.requireAnnotations`).
- [Cookie Interceptor](cookie-interceptor.html) — cookie values.
- [JSON](../../plugins/json) and [REST](../../plugins/rest) plugins —
per-property
- authorization performed during deserialization, so unauthorized fields are
never set.
+ authorization performed during deserialization, so an unauthorized property
is not set on
+ the target object. This covers the properties the deserializer binds **by
name**; in the
+ REST plugin a Jackson any-setter is a separate sink that is not covered — see
+ [Jackson any-setters](#jackson-any-setters) below.
### Creator-bound properties
@@ -48,6 +51,27 @@ the same way as any nested object: `@StrutsParameter(depth =
...)` on the getter
model. Otherwise those values silently stop arriving.
{:.alert .alert-warning}
+### Jackson any-setters
+
+A class that declares a Jackson any-setter — `@JsonAnySetter` on a method, on
a field, or on a
+`@JsonCreator` parameter — tells Jackson to route **every otherwise-unknown
key** in the request body
+to that member. The REST plugin's authorization wrapper covers the properties
Jackson binds by name;
+an any-setter is a separate sink and is not wrapped. Keys arriving through it
are therefore set
+without an `@StrutsParameter` check, even with
`struts.parameters.requireAnnotations` enabled, and
+even in the same request in which an ordinary unannotated setter on the same
class is correctly
+rejected.
+
+Two limits are worth knowing. An any-setter beneath an **unauthorized parent**
is still unreachable:
+the parent is rejected first and its whole subtree is skipped. And
`@JsonUnwrapped` is a named
+property, so it is unaffected by this.
+
+Declaring an any-setter on a class bound from a REST request body is the
application accepting
+arbitrary names and values off the wire — the same decision as binding a
`Map`, and it deserves the
+same scrutiny. Where that is not what you want, do not declare one on a
request-bound class, or
+narrow what the method accepts before storing it. Tracked as
+[WW-5712](https://issues.apache.org/jira/browse/WW-5712).
+{:.alert .alert-warning}
+
## ModelDriven actions
When an action implements `ModelDriven` and the [Model Driven