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]
