Lukasz Lenart created WW-5747:
---------------------------------

             Summary: RedactionAwareDeserializer leaves the parser mid-object 
when it drops a REST body object
                 Key: WW-5747
                 URL: https://issues.apache.org/jira/browse/WW-5747
             Project: Struts 2
          Issue Type: Bug
          Components: Plugin - REST
            Reporter: Lukasz Lenart


h2. Summary

{{RedactionAwareDeserializer}} wraps every bean deserializer in the REST 
plugin's Jackson authorization and, when construction fails after a 
{{@StrutsParameter}} redaction marked its scope, catches the 
{{JsonMappingException}} and returns {{null}} so the whole object is dropped 
rather than partially exposed. It returns without moving the parser: whatever 
tokens of the dropped object remain unread are then consumed by the _enclosing_ 
bean's property loop.

h2. Current behaviour

Any failure that fires before the dropped object's last field desyncs the read. 
Two shapes reach it:
* a creator-bound bean whose construction fails when its last creator parameter 
arrives (a redacted primitive under {{FAIL_ON_NULL_FOR_PRIMITIVES}}, a compact 
constructor rejecting {{null}}) while non-creator fields follow it in the body;
* a bean whose property-based {{@JsonIdentityInfo}} id is a bean that itself 
failed to construct on a redacted member: the {{null}} id fails Jackson's 
binding at the {{id}} property (WW-5746).

Reproduced with {{record StrictKey(String k)}} (rejects {{null}}) as the id of 
{{StrictIdentified { id; name }}}, held in {{StrictHolder { child; other; name 
}}}, body {{{"child":{"id":{"k":"a"},"name":"x"},"other":"o"}}} and 
{{child.id.k}} denied: {{child}} is {{null}} as intended, but the child's 
{{name}} value {{"x"}} was bound to the *holder's* {{name}}, and the holder's 
own {{other}} was lost because its object read ended at the child's closing 
brace. Without a same-named property on the parent the stray field raises 
"Unrecognized field", which — the parent's scope being marked redacted — 
silently drops the whole root object instead.

The misplaced value still passes the parent property's own {{@StrutsParameter}} 
check at its own path, so this is data landing in the wrong property, not an 
authorization bypass.

h2. Proposed change

Resync before returning {{null}}: record the parser's nesting depth (and 
whether the entry token was {{START_OBJECT}}, {{START_ARRAY}} or a 
{{FIELD_NAME}} inside the object) when the wrapper is entered, and on a 
swallowed failure skip tokens — {{skipChildren()}} on any nested start token — 
until the matching {{END_OBJECT}}/{{END_ARRAY}} at that depth has been 
consumed. A scalar entry token needs no resync. Cover both shapes above with 
tests, asserting the parent's later properties still bind and the dropped 
object's fields never reach the parent.

h2. Compatibility notes

No configuration or API change. Only bodies that already trigger an object drop 
are affected, and only in that the enclosing object is now read correctly 
afterwards.



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

Reply via email to