WW-5725 Authorize the buffered creator path in AuthorizingSettableBeanProperty - #1916
Draft
lukaszlenart wants to merge 1 commit into
Draft
WW-5725 Authorize the buffered creator path in AuthorizingSettableBeanProperty#1916lukaszlenart wants to merge 1 commit into
lukaszlenart wants to merge 1 commit into
Conversation
…SettableBeanProperty AuthorizingSettableBeanProperty authorized a property in deserializeAndSet and 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 PropertyValue.Regular.assign -> 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 now 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 rather than the deserializer's handled type. On the direct path it re-checks a path deserializeAndSet already accepted; the authorizer call 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 cover the 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 in the direction that matters: a sibling grant on the enclosing bean must not authorize a collapsed nested path. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Fixes WW-5725
AuthorizingSettableBeanPropertyauthorized a property indeserializeAndSet/deserializeSetAndReturnand 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 thefinalSettableBeanProperty.deserialize()with no authorization and no path push, and assigned after construction throughSettableBeanProperty.set(), which theDelegatingbase 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/deserializeSetAndReturnkeep authorizing the direct path and skipping the value on rejection, so the setter never fires, but no longer push the path themselves.AuthorizingValueDeserializerwraps 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-placedeserialize(p, ctxt, intoValue)used for setterless collections, anddeserializeWithTypefor polymorphic properties — and classifies the[0]element prefix on the property's declared type. On the direct path it re-checks a pathdeserializeAndSetalready accepted; the authorizer is stateless, so the answer is the same.set/setAndReturnauthorize the already-materialized assignment, which also covers a bufferednull(Jackson skips the value deserializer for a null token) and the other callers ofset()in jackson-databind:@JsonMerge,@JsonManagedReference, inner-class valued properties,EXTERNAL_PROPERTYtype ids and the@JsonIdentityInfoid 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@JsonIdentityInfoid property now needs@StrutsParameterlike 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 otherset()callers.🤖 Generated with Claude Code