Skip to content

WW-5642 fix(rest): authorize @StrutsParameter on record/creator-bound REST body properties#1774

Open
g0w6y wants to merge 1 commit into
apache:mainfrom
g0w6y:fix/rest-jackson-creator-property-authorization
Open

WW-5642 fix(rest): authorize @StrutsParameter on record/creator-bound REST body properties#1774
g0w6y wants to merge 1 commit into
apache:mainfrom
g0w6y:fix/rest-jackson-creator-property-authorization

Conversation

@g0w6y

@g0w6y g0w6y commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Fixes WW-5642

Summary

ParameterAuthorizingModule enforces @StrutsParameter on REST/JSON body deserialization by wrapping each Jackson property's deserializeAndSet / deserializeSetAndReturn (AuthorizingSettableBeanProperty). Jackson never calls either method for creator-bound properties — Java records, @JsonCreator constructors, @ConstructorProperties — it instead calls SettableBeanProperty#deserialize directly, which Jackson declares final, so the existing wrapper cannot intercept it.

Practical effect: with struts.parameters.requireAnnotations enabled, any record-typed field anywhere in a REST action's request body (top-level or nested) is populated with no @StrutsParameter check at all, silently defeating the protection for that entire subtree. This is easy to hit unintentionally — record is the idiomatic way to model immutable REST DTOs on the Java versions Struts now targets.

Verified with a live reproduction before writing the fix: a record field with an unauthorized component was populated anyway; after the fix, the same component is correctly rejected while an authorized sibling component still passes through.

Fix

Add AuthorizingValueDeserializer, which wraps the property's value deserializer rather than the SettableBeanProperty — the value deserializer is what the final deserialize() method delegates to, so wrapping it is the only available interception point for the creator-bound path. It is installed from AuthorizingSettableBeanProperty#withValueDeserializer, scoped to CreatorProperty specifically, so ordinary setter/field/builder-pattern properties — already correctly authorized via the existing deserializeAndSet/deserializeSetAndReturn overrides — are not checked a second time (which would also double-push the path-authorization stack and produce incorrect nested paths).

Test plan

  • New regression test testRecordComponentAuthorizedByPath in ParameterAuthorizingModuleTest: a record-typed nested property with one authorized and one unauthorized component — confirms the unauthorized component is rejected and the authorized one still deserializes.
  • Full existing ParameterAuthorizingModuleTest suite (setter, builder, nested, collection, map, array authorization paths) passes unchanged — confirms no regression from scoping the new check to creator-bound properties only.
  • Full struts2-rest-plugin module test suite passes (106/106).

…dy properties

ParameterAuthorizingModule enforces @StrutsParameter on REST/JSON body
deserialization by wrapping each property's deserializeAndSet/
deserializeSetAndReturn. Jackson never calls either method for
creator-bound properties (Java records, @JsonCreator constructors,
@ConstructorProperties) — it calls SettableBeanProperty#deserialize
directly, which is declared final and bypasses the wrapper entirely.
With struts.parameters.requireAnnotations enabled, any record-typed
field anywhere in a REST action's request body was populated with no
authorization check at all.

Add AuthorizingValueDeserializer, which wraps the property's value
deserializer instead of the property itself, and install it from
AuthorizingSettableBeanProperty#withValueDeserializer — scoped to
CreatorProperty so ordinary setter/field/builder properties, already
authorized via the existing wrapper, aren't checked twice.
@lukaszlenart

Copy link
Copy Markdown
Member

Thanks for the PR but please create a JIRA ticket to cover this work 🙏

@g0w6y g0w6y changed the title fix(rest): authorize @StrutsParameter on record/creator-bound REST body properties WW-5642 fix(rest): authorize @StrutsParameter on record/creator-bound REST body properties Jul 10, 2026
@g0w6y

g0w6y commented Jul 10, 2026

Copy link
Copy Markdown
Contributor Author

Created WW-5642 to track this: https://issues.apache.org/jira/browse/WW-5642. Ready for review/merge whenever you get a chance — thanks!

@g0w6y

g0w6y commented Jul 10, 2026

Copy link
Copy Markdown
Contributor Author

Given this fixes an authorization bypass where unauthorized REST body fields could be set, should we handle this as a security advisory instead of a normal fix? Happy to follow whichever process you prefer, just let me know.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants