Lukasz Lenart created WW-5726:
---------------------------------

             Summary: Merged polymorphic property with a non-null initial value 
is not authorized in the REST plugin
                 Key: WW-5726
                 URL: https://issues.apache.org/jira/browse/WW-5726
             Project: Struts 2
          Issue Type: Bug
            Reporter: Lukasz Lenart
             Fix For: 7.4.0


h2. Summary

A property that is mergeable ({{@JsonMerge}}, or merge enabled through 
configuration), carries a value type deserializer ({{@JsonTypeInfo}} on the 
property or its declared type), and has a non-null initial value is 
deserialized by Jackson through a path that reaches neither 
{{AuthorizingSettableBeanProperty}} nor {{AuthorizingValueDeserializer}}. The 
property is assigned without an authorization check, and its members are 
authorized against the enclosing prefix instead of their own.

Sibling of WW-5725, which covers the other {{set()}} and value-deserializer 
entry points; this one cannot be closed from the property wrapper.

h2. Current behaviour

{{MergingSettableBeanProperty}} is constructed by 
{{BeanDeserializerBase.resolve()}} around the already-wrapped property, so 
{{BeanDeserializer}} calls its {{deserializeAndSet}}, which never reaches 
{{AuthorizingSettableBeanProperty.deserializeAndSet}}. With a non-null current 
value it calls the {{final}} {{SettableBeanProperty.deserializeWith(p, ctxt, 
oldValue)}}. When {{_valueTypeDeserializer}} is set, that method derives the 
subtype from {{oldValue.getClass()}}, resolves a fresh contextual deserializer 
for it, and deserializes in place into {{oldValue}} — bypassing the property's 
{{_valueDeserializer}} (where {{AuthorizingValueDeserializer}} sits). Because 
the merge is in place, {{newValue == oldValue}} and {{set()}} is not called 
either.

Consequences: the property itself is never authorized, and no path is pushed, 
so the merged bean's members are authorized as {{member}} at the enclosing 
prefix rather than {{property.member}}. A grant on a sibling property of the 
enclosing bean with the same member name therefore authorizes the nested write. 
No type id is required in the body; the subtype comes from the existing value.

The non-polymorphic merge (no type deserializer) goes through 
{{_valueDeserializer.deserialize(p, ctxt, oldValue)}} and is covered by WW-5725.

Applies to JSON and XML bodies alike. 6.x is unaffected: the REST plugin there 
has no Jackson authorization module.

h2. Proposed change

Both facts are known when {{ParameterAuthorizingModule.updateBuilder}} runs: 
{{property.getMetadata().getMergeInfo() != null}} and 
{{property.hasValueTypeDeserializer()}}. Refuse the combination there, so the 
definition fails closed rather than binding unchecked — either by reporting a 
bad definition or by stripping the merge for that property so it takes the 
ordinary authorized path.

The alternative of intercepting the in-place typed deserialize from 
{{RedactionAwareDeserializer.createContextual}} would keep the merge working, 
but it has to avoid a second path push on the normal path and is the more 
fragile of the two.

Add a test in the same direction as the WW-5725 tests: a {{@JsonMerge}} 
{{@JsonTypeInfo}} property with an initialised field, an authorizer granting 
the enclosing bean's sibling member of the same name, asserting the nested 
member is not assigned.

h2. Compatibility notes

An application that relies on merging into a polymorphic property with a 
non-null initial value through the REST plugin will see that definition 
rejected (or merge disabled for it) until the property is given a null initial 
value or the type deserializer is dropped. No other property kind is affected.



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

Reply via email to