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

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

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

   Fixes [WW-5745](https://issues.apache.org/jira/browse/WW-5745)
   
   ## Problem
   
   When a property refers to an object by `@JsonIdentityInfo` id before that 
object has appeared in the body, Jackson defers the assignment: 
`ObjectIdReferenceProperty` registers a referring on the id and, once the 
object is deserialized, assigns it through the property's `set()` from wherever 
in the body the object turned up. `AuthorizingSettableBeanProperty` authorized 
that write with `pathFor(memberName)` against the path prefix and dynamic-key 
scope current *at that moment* — the target object's location, not the 
referring property's. The property had already been authorized under its own 
path when it was read, so the second check could only reject: a correctly 
granted forward reference across nesting depths (`people[0].friend` → `boss`, 
checked as `boss.friend`), or one resolved inside an `allowDynamicKeys` 
subtree, was dropped with a WARN naming the target's path.
   
   Surfaced by the WW-5727 reviews; pre-existing and independent of that fix.
   
   ## Change
   
   - `AuthorizingValueDeserializer` catches the `UnresolvedForwardReference` 
after the property has been authorized and, before rethrowing, records the 
verdict in the new `AuthorizedForwardReferences` (thread-local) against the 
awaited id and the member name.
   - `AuthorizingSettableBeanProperty.set`/`setAndReturn` find that entry by 
the object the id resolved to — the one they receive — and skip the second 
check. The deferred write is the read completing, not a new assignment to 
authorize. Keying by the id rather than the referring bean also covers a 
creator-bound referrer, which is not constructed yet when the reference is read.
   - `ParameterAuthorizingModule.clearAuthorizationContext()` clears the 
entries with the dynamic-key state; `ContentTypeInterceptor` now calls the new 
static `clearRequestState()` when it unbinds the context as well, so a 
third-party handler that registers the module without clearing cannot leave 
verdicts on the thread.
   
   Not affected: collection/map element forward references (resolved inside 
their own deserializers, never reach the property wrapper); a custom 
`ObjectIdResolver` that does not hand back the bound object leaves the write on 
today's check (fail-closed, documented on the class).
   
   Two earlier shapes were rejected in review: re-authorizing under a recorded 
*path* still used the bind-time dynamic-key scope, and a `Referring` registered 
on the id duplicated every unresolved id in Jackson's error report; keying by 
`JsonParser.currentValue()` mis-attributed to the parent bean for a 
creator-bound referrer in one ordering.
   
   ## Security review
   
   Two independent passes over the final mechanism, both clean: every `set()` 
of a bean value is preceded by the read-time gate under the property's own 
path, so the skip only removes the redundant second check and a stale entry can 
never grant what the read denied.
   
   ## Tests
   
   `ParameterAuthorizingModuleTest`: nested referrer → root target resolves 
under its own path; referrer not granted at its own path stays null; 
creator-bound referrer; target resolved inside an any-setter dynamic-key scope; 
no-context pass-through; request-boundary clear. 
`ContentTypeInterceptorIntegrationTest`: the interceptor clears state a handler 
left behind.
   
   `mvn test -DskipAssembly -pl plugins/rest`: 199 tests, 0 failures.
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)




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

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

> REST plugin authorizes a forward-referenced object-id property at the depth 
> where the reference resolves
> --------------------------------------------------------------------------------------------------------
>
>                 Key: WW-5745
>                 URL: https://issues.apache.org/jira/browse/WW-5745
>             Project: Struts 2
>          Issue Type: Bug
>          Components: Plugin - REST
>            Reporter: Lukasz Lenart
>            Assignee: Lukasz Lenart
>            Priority: Minor
>             Fix For: 7.4.0
>
>          Time Spent: 10m
>  Remaining Estimate: 0h
>
> h2. Summary
> When a property refers to an object by {{@JsonIdentityInfo}} id before that 
> object has appeared in the body, Jackson defers the assignment: 
> {{ObjectIdReferenceProperty}} registers a {{PropertyReferring}} and, once the 
> referenced object is deserialized, calls the referring property's {{set()}} 
> from {{handleResolvedForwardReference}}. That call happens wherever the 
> referenced object turns up in the document, which can be at a different 
> nesting depth than the referring property.
> {{AuthorizingSettableBeanProperty.set()}} authorizes with 
> {{ParameterAuthorizationContext.pathFor(memberName)}}, i.e. the member name 
> appended to whatever path prefix is on the stack _at the time of the call_. 
> For a deferred assignment that prefix belongs to the object being 
> deserialized when the reference resolves, not to the bean that owns the 
> referring property, so the path checked is wrong.
> Surfaced by the WW-5727 reviews; pre-existing and independent of that fix.
> h2. Current behaviour
> The mis-computed path can only add a rejection (the wrong prefix is unlikely 
> to be granted), so this fails closed: a correctly annotated forward reference 
> may be dropped with a WARN naming a path the developer never wrote. The 
> first, non-deferred read of the referring property is authorized at the 
> correct path through {{AuthorizingValueDeserializer}}, so no unauthorized 
> value is assigned.
> h2. Proposed change
> Capture the path prefix when the referring property is first read and use it 
> when the deferred {{set()}} runs — e.g. wrap the {{Referring}} Jackson 
> registers, or record the prefix on the wrapper before the reference is 
> deferred. Add a test with two identified objects where the reference appears 
> before its target at a different depth, asserting the referring property 
> binds under its own path when granted and is rejected when not.
> h2. Compatibility notes
> No configuration or API change. Only forward references across nesting depths 
> are affected, and only in the direction of a spurious rejection.



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

Reply via email to