[ 
https://issues.apache.org/jira/browse/WW-5624?focusedWorklogId=1014321&page=com.atlassian.jira.plugin.system.issuetabpanels:worklog-tabpanel#worklog-1014321
 ]

ASF GitHub Bot logged work on WW-5624:
--------------------------------------

                Author: ASF GitHub Bot
            Created on: 09/Apr/26 16:37
            Start Date: 09/Apr/26 16:37
    Worklog Time Spent: 10m 
      Work Description: lukaszlenart commented on PR #1657:
URL: https://github.com/apache/struts/pull/1657#issuecomment-4215870666

   The new PR is much closer to the right direction than `#1651` / `#1652` 
because it extracts shared authorization logic and avoids duplicating ad hoc 
checks in each plugin. I still see a few remaining gaps that should be 
addressed before this is fully aligned with `ParametersInterceptor` semantics:
   
   issue (blocking): The `ModelDriven` exemption in 
`DefaultParameterAuthorizer` looks too broad. Using `target != action` as the 
signal means any non-action JSON root object can be treated like a 
`ModelDriven` model and bypass `@StrutsParameter` checks. I think this should 
mirror the existing `ParametersInterceptor` semantics more closely, instead of 
inferring `ModelDriven` purely from object identity.
   
   issue (blocking): `JSONInterceptor.filterUnauthorizedKeys()` only filters 
top-level JSON keys. That improves the previous behavior, but it still does not 
enforce nested `@StrutsParameter(depth=N)` semantics. Once an allowed top-level 
key remains in the map, `JSONPopulator` can still recursively populate the 
nested object graph underneath it. I think this needs path-aware authorization, 
not only root-key filtering. Either walk nested maps/lists and authorize 
synthesized paths recursively, or move the authorization check into the 
recursive population flow so each nested segment is checked against the same 
depth rules as `ParametersInterceptor`.
   
   issue (blocking): The two-phase REST approach in 
`ContentTypeInterceptor.copyAuthorizedProperties()` is better than the previous 
direct merge, but it still authorizes only top-level bean properties. If an 
allowed property is a complex object, the full nested graph deserialized into 
the staging instance is copied over in one step without nested authorization. I 
think the copy phase needs to become a deep authorized merge, not a shallow 
bean-property copy.
   
   issue: `copyAuthorizedProperties()` currently skips `null` values, which 
changes semantics when `struts.parameters.requireAnnotations=true`. That means 
an explicitly provided `null` in the request body can no longer clear an 
authorized property. Please confirm whether this behavior change is 
intentional. If not, explicit nulls for authorized properties should probably 
still be copied. If it is intentional, it should be documented and covered by 
tests.
   
   issue: The two-phase deserialization path now requires a public no-arg 
constructor on the target type. That is a new constraint which may break 
existing actions/models when `requireAnnotations=true`. Please verify whether 
this constructor requirement is acceptable for Struts REST usage. If it is, I 
would at least add explicit tests and mention it in the PR/JIRA notes as a 
compatibility constraint.
   
   suggestion: Please also add regression tests for transition mode with nested 
JSON bodies, and for `JSONInterceptor.root` resolving to a non-action object. 
Those are the cases most likely to expose the remaining semantic gaps.




Issue Time Tracking
-------------------

    Worklog Id:     (was: 1014321)
    Time Spent: 1h 10m  (was: 1h)

> Request body population bypasses @StrutsParameter contract outside 
> ParametersInterceptor
> ----------------------------------------------------------------------------------------
>
>                 Key: WW-5624
>                 URL: https://issues.apache.org/jira/browse/WW-5624
>             Project: Struts 2
>          Issue Type: Bug
>          Components: Plugin - JSON, Plugin - REST
>    Affects Versions: 7.1.1
>            Reporter: Tran Quac
>            Priority: Major
>             Fix For: 7.2.0
>
>          Time Spent: 1h 10m
>  Remaining Estimate: 0h
>
> h2. Summary
> {{@StrutsParameter}} enforcement is currently implemented in 
> {{ParametersInterceptor}} for standard request parameter binding, but 
> request-body based binders in some plugins bypass that authorization model 
> and populate action/model objects directly.
> This creates inconsistent behavior between URL/form parameters and JSON/XML 
> request bodies, and may allow mass assignment of properties that would 
> normally be rejected by {{ParametersInterceptor}}.
> h2. Affected areas currently identified
> * JSON plugin:
> {{JSONPopulator.populateObject()}} sets properties via direct reflection and 
> does not follow the full {{@StrutsParameter}} authorization rules.
> * REST plugin:
> {{JacksonJsonHandler.toObject()}} updates target objects directly via Jackson 
> and does not follow the full {{@StrutsParameter}} authorization rules.
> h2. Problem scope
> The issue is broader than checking whether a setter is annotated. The current 
> core contract in {{ParametersInterceptor}} also includes:
> * permitted nesting depth
> * authorization based on the exposed root member
> * ModelDriven handling
> * transition mode semantics
> * related allowlisting behavior
> Any request-body binding implementation should align with that same contract, 
> otherwise Struts applies different security rules depending on how input 
> reaches the action/model.
> h2. Expected direction
> Instead of implementing separate partial checks in each plugin, Struts should 
> reuse or extract the shared parameter-binding authorization logic from 
> {{ParametersInterceptor}} and apply it consistently across request-body 
> binders.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to