You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
AC: resolve a shadowed field name to the most-derived declaration instead of the inherited one #149
Fields are addressed by name throughout ReflectionUtils, and the map backing that lookup was
built by visiting a class's own fields first and then its superclasses — so for a field
shadowing an inherited one of the same name, the inherited declaration overwrote the
subclass one and won. That is backwards from Java, where this.field refers to the most-derived
declaration, and it is the field a serializer emits.
Three consequences, in order of severity. The @AccessControl annotation on a subclass field
was never seen, so the rule silently did not exist — no warning, no error, restricted data
served. Reads returned the inherited field's value, so access control inspected the wrong one.
Writes blanked the inherited field and left the visible one populated:
before: resolves to Base reads BASE-SECRET nulling left CHILD-SECRET visible
after: resolves to Child reads CHILD-SECRET nulling blanked the visible field
Visiting superclasses first fixes all three, so the most-derived declaration wins.
The ordering fix alone is not sufficient, which is worth stating plainly: a Map<String, Field> holds one field per name, so whichever declaration wins, the other becomes
unreachable. An @AccessControl on the inherited field is still lost when a subclass
shadows it. Preferring the most-derived one is correct — it is the field that reaches the
response — but it cannot make both visible.
So the complement: ReflectionUtils.shadowedFieldNames(Class) reports names declared more than
once in a hierarchy, and access control warns when any declaration of a shadowed name carries @AccessControl, telling the user to rename one of them. That turns a silent fail-open into a
startup warning, consistent with how the other unenforceable-requirement cases are handled.
Triggering this needs field shadowing in a DTO hierarchy, which is rare and a code smell in its
own right — presumably why it has gone unnoticed. The blast radius of the fix is limited to
shadowed names: findField, both getters, both setters, fetchFieldTypes, fetchFields and fetchAnnotationForFields behave identically for every other field.
Found while investigating whether ObjectCopier should be folded into ReflectionUtils. ObjectCopier was never affected — it keeps an ordered list rather than a name-keyed map, and
blanks both declarations, which fails closed.
Activity
#149: resolve a shadowed field name to the most-derived declaration i…