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

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

                Author: ASF GitHub Bot
            Created on: 11/Sep/26 14:43
            Start Date: 11/Sep/26 14:43
    Worklog Time Spent: 10m 
      Work Description: lukaszlenart opened a new pull request, #1916:
URL: https://github.com/apache/struts/pull/1916

   Fixes [WW-5725](https://issues.apache.org/jira/browse/WW-5725)
   
   `AuthorizingSettableBeanProperty` authorized a property in 
`deserializeAndSet` / `deserializeSetAndReturn` and wrapped the value 
deserializer only for creator-bound properties. Jackson takes neither route for 
a non-creator property it buffers during property-based creation: a setter 
property that appears in the body before the last creator parameter is read 
through the `final` `SettableBeanProperty.deserialize()` with no authorization 
and no path push, and assigned after construction through 
`SettableBeanProperty.set()`, which the `Delegating` base forwarded unchecked. 
The same property after the last creator parameter, or on a setter-only type, 
was already rejected, so member order alone decided whether the check applied, 
and the members of a buffered bean-valued property were checked one level too 
shallow.
   
   Three gates now cover the paths between them:
   
   - `deserializeAndSet` / `deserializeSetAndReturn` keep authorizing the 
direct path and skipping the value on rejection, so the setter never fires, but 
no longer push the path themselves.
   - `AuthorizingValueDeserializer` wraps every property's value deserializer, 
not only creator properties, and owns the path push for nested members. It 
covers all three of Jackson's entry points — `deserialize(p, ctxt)`, the 
in-place `deserialize(p, ctxt, intoValue)` used for setterless collections, and 
`deserializeWithType` for polymorphic properties — and classifies the `[0]` 
element prefix on the property's declared type. On the direct path it re-checks 
a path `deserializeAndSet` already accepted; the authorizer is stateless, so 
the answer is the same.
   - `set` / `setAndReturn` authorize the already-materialized assignment, 
which also covers a buffered `null` (Jackson skips the value deserializer for a 
null token) and the other callers of `set()` in jackson-databind: `@JsonMerge`, 
`@JsonManagedReference`, inner-class valued properties, `EXTERNAL_PROPERTY` 
type ids and the `@JsonIdentityInfo` id property, none of which were authorized 
before.
   
   All gates go through `DynamicKeyAuthorizationContext`, so a dynamic-key 
scope authorizes by depth on every path.
   
   **Tests** (all in `ParameterAuthorizingModuleTest`): setter before and after 
the last creator parameter, a setter-only type with the same member order, a 
nested creator, a creator-plus-setter type inside a dynamic-key scope, a 
buffered bean-valued setter whose members must be authorized at their own 
depth, a setterless collection, and a polymorphic property. The last two are 
written in the direction that matters — a sibling grant on the enclosing bean 
must not authorize a collapsed nested path — and both failed against an 
intermediate version of this change that had moved the push but not yet covered 
the 3-arg and typed entry points.
   
   **Behaviour notes.** No configuration or API change. A request that 
previously had an unauthorized property assigned only because of its position 
in the body now has it dropped, consistent with every other position. 
Properties assigned through the other `set()` callers listed above are now 
subject to the same authorization as ordinary setters — most visibly, an 
`@JsonIdentityInfo` id property now needs `@StrutsParameter` like any other. A 
buffered rejection logs twice (once at the read, once at the assignment); the 
assignment-side warning is kept because it is the only one for the other 
`set()` callers.
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)
   




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

            Worklog Id:     (was: 1040961)
    Remaining Estimate: 0h
            Time Spent: 10m

> AuthorizingSettableBeanProperty does not authorize the buffered set() path in 
> the REST plugin
> ---------------------------------------------------------------------------------------------
>
>                 Key: WW-5725
>                 URL: https://issues.apache.org/jira/browse/WW-5725
>             Project: Struts 2
>          Issue Type: Bug
>            Reporter: Lukasz Lenart
>            Priority: Major
>             Fix For: 7.4.0
>
>          Time Spent: 10m
>  Remaining Estimate: 0h
>
> h2. Summary
> {{AuthorizingSettableBeanProperty}} authorizes a property in 
> {{deserializeAndSet}} and {{deserializeSetAndReturn}}, and wraps the value 
> deserializer for creator-bound properties. It does not override {{set(Object, 
> Object)}} or {{setAndReturn(Object, Object)}}, which is the path Jackson 
> takes for a non-creator property it has buffered during property-based 
> creation. That property is then assigned without an authorization check.
> h2. Current behaviour
> On a type that mixes a property-based creator ({{@JsonCreator}} constructor 
> or a record) with an ordinary setter, jackson-databind buffers a setter 
> property that appears in the JSON before the last creator parameter, 
> constructs the instance, and assigns the buffered value through 
> {{PropertyValue.Regular.assign}} → {{SettableBeanProperty.set()}}. 
> {{SettableBeanProperty.Delegating.set()}} forwards straight to the delegate, 
> so the authorization in {{AuthorizingSettableBeanProperty}} never runs for 
> it. {{withValueDeserializer}} is scoped to {{CreatorProperty}}, so the setter 
> property keeps its plain value deserializer, and 
> {{SettableBeanProperty.deserialize}} is {{final}} and cannot be intercepted.
> The same property is rejected when it appears after the last creator 
> parameter (direct {{deserializeAndSet}} path), and on a setter-only type with 
> the same payload. The order of members in the request body alone decides 
> whether the check applies. Nothing is logged when the buffered assignment 
> goes through.
> Applies to JSON and XML bodies alike, since {{JacksonXmlHandler}} registers 
> the same module. 6.x is unaffected: the REST plugin there has no Jackson 
> authorization module.
> h2. Proposed change
> Override {{set(Object, Object)}} and {{setAndReturn(Object, Object)}} in 
> {{AuthorizingSettableBeanProperty}} and apply the same authorization used by 
> {{deserializeAndSet}}, dropping the value and logging the rejection the same 
> way. That closes the buffered path with no new configuration: the guard 
> already rejects the identical property in the other member order, so no 
> legitimate behaviour depends on the gap.
> Add tests covering a creator-plus-setter type with the unauthorized setter 
> before and after the last creator parameter, top-level and nested, for both 
> handlers.
> h2. Compatibility notes
> No configuration or API change. A request that previously had an unauthorized 
> property assigned only because of its position in the body now has it 
> dropped, consistent with every other position.
> Reported by 0xCc.zhang.



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

Reply via email to