[
https://issues.apache.org/jira/browse/WW-5709?focusedWorklogId=1041212&page=com.atlassian.jira.plugin.system.issuetabpanels:worklog-tabpanel#worklog-1041212
]
ASF GitHub Bot logged work on WW-5709:
--------------------------------------
Author: ASF GitHub Bot
Created on: 13/Sep/26 16:03
Start Date: 13/Sep/26 16:03
Worklog Time Spent: 10m
Work Description: lukaszlenart opened a new pull request, #1931:
URL: https://github.com/apache/struts/pull/1931
Fixes [WW-5709](https://issues.apache.org/jira/browse/WW-5709)
`java.beans.Introspector` only records a write method for a `void` setter,
while OGNL binds through any public one-argument `setX` method regardless of
its return type. `StrutsParameterAuthorizer` derived its view of what a target
can take from `PropertyDescriptor.getWriteMethod()`, so a fluent setter was
invisible to it:
1. `@StrutsParameter` on a fluent setter never counted — the parameter was
rejected unless the backing field was annotated too.
2. On a `ModelDriven` action, an unannotated fluent setter on the *action*
looked declared on neither the model nor the action and took the
custom-accessor fallback WW-5698 left for Map-backed models, so it bound.
### Change
`findBindableAccessor` resolves the depth-0 accessor the way OGNL does —
from the already-cached `BeanInfo`'s method descriptors, by name and arity,
public and non-static — and both `declaresProperty` and
`hasValidAnnotatedMember` go through it. Depth ≥ 1 still uses the read method,
so `OgnlParameterAllowlister` is untouched.
Where several setters qualify, the most-derived declaration wins (an
annotated override of a generic base's erased `setModel(Object)` is the one
judged), and among overloads at that level an annotated one (a convenience
`setAge(int)` beside `@StrutsParameter setAge(String)` no longer turns the
property away — that would have been a regression against `main`).
`hasValidAnnotatedPropertyDescriptor` is kept as a delegating shim,
`@Deprecated(forRemoval = true)`; removal is
[WW-5739](https://issues.apache.org/jira/browse/WW-5739) at 8.0.0.
### Behaviour change
With `struts.parameters.requireAnnotations=true`: annotated fluent setters
now bind; an unannotated fluent setter on a `ModelDriven` action is now
rejected like a void one. Relative to 7.3.0 this is part of the WW-5698 change
and rides on its migration note.
### Tests
- `ParameterAuthorizerTest`: annotated / unannotated fluent setter, fluent
setter on a `ModelDriven` action (rejected unannotated, authorized annotated),
fluent setter on the model shadowing an unannotated action setter, static
`setX` namesake (still no exemption — proven load-bearing), generic-override
and same-class-overload preference.
-
`ParametersInterceptorTest.testModelDrivenFluentSetterOnActionRequiresAnnotation`:
end-to-end through the real OGNL stack; RED on `main` (`secret` bound through
the fluent setter), paired with two positive binds so the negative is not
vacuous.
`core`, `plugins/json`, `plugins/rest` test suites pass.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Issue Time Tracking
-------------------
Worklog Id: (was: 1041212)
Remaining Estimate: 0h
Time Spent: 10m
> @StrutsParameter enforcement does not recognise fluent (non-void) setters
> -------------------------------------------------------------------------
>
> Key: WW-5709
> URL: https://issues.apache.org/jira/browse/WW-5709
> Project: Struts 2
> Issue Type: Task
> Components: Core
> Reporter: Lukasz Lenart
> Priority: Major
> Fix For: 7.4.0
>
> Time Spent: 10m
> Remaining Estimate: 0h
>
> {{java.beans.Introspector}} treats a {{set*}} method as a property write
> method only when its return type is {{void}}. OGNL does not:
> {{OgnlRuntime._getSetMethod}} matches a setter on name and argument count
> alone, with no return-type check.
> {{StrutsParameterAuthorizer}} derives its view of what a target can bind from
> {{java.beans}} property descriptors, so a fluent setter such as
> public MyAction setFoo(String foo) { this.foo = foo; return this; }
> is invisible to the authorizer while remaining perfectly bindable by OGNL.
> Two symptoms follow from that one cause.
> h3. 1. {{@StrutsParameter}} on a fluent setter has no effect
> {{hasValidAnnotatedPropertyDescriptor}} reads {{propDesc.getWriteMethod()}},
> which is {{null}} here, so it returns {{false}} and the check falls through
> to {{hasValidAnnotatedField}}. Unless the backing field also carries the
> annotation, the parameter is rejected even though the developer annotated the
> setter. This fails closed, but it is silent and hard to diagnose.
> h3. 2. On a ModelDriven action, an unannotated fluent setter on the action
> escapes the requirement
> {{declaresProperty}}, added in WW-5698, asks the same {{java.beans}} question
> to decide whether a property belongs to the model or to the action. A fluent
> setter on the action answers "neither", so the parameter takes the "declared
> on neither the model nor the action" fallback that exists for custom OGNL
> property accessors, and is exempted. OGNL then binds it through the fluent
> setter on the action.
> This is not a regression: before WW-5698 every parameter on a ModelDriven
> action was exempt unconditionally, so it is no more permissive than the
> previous behaviour. It is the one case where WW-5698 does not fully reach its
> stated goal of subjecting a ModelDriven action's own members to the
> annotation requirement.
> h3. Suggested fix
> Identify setters the way OGNL does — by name and arity rather than by
> {{java.beans}} write methods — in both
> {{StrutsParameterAuthorizer.declaresProperty}} and
> {{hasValidAnnotatedPropertyDescriptor}}, so the authorizer's model of what is
> bindable matches what OGNL will actually bind.
> Found while reviewing WW-5698 / PR
> [#1872|https://github.com/apache/struts/pull/1872].
--
This message was sent by Atlassian Jira
(v8.20.10#820010)