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)


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to