This is an automated email from the ASF dual-hosted git repository. kusal pushed a commit to branch WW-5428-allowlist-hibernate in repository https://gitbox.apache.org/repos/asf/struts.git
commit 1c25b0537f63a28ca25db39d146b4595487fb032 Author: Kusal Kithul-Godage <g...@kusal.io> AuthorDate: Mon Jun 17 21:02:49 2024 +1000 WW-5428 Allowlist capability should resolve Hibernate proxies when disableProxyObjects is not set --- .../xwork2/ognl/SecurityMemberAccess.java | 10 +++++++ .../com/opensymphony/xwork2/util/ProxyUtil.java | 33 ++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java b/core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java index f882b2c58..db0598541 100644 --- a/core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java +++ b/core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java @@ -209,6 +209,16 @@ public class SecurityMemberAccess implements MemberAccess { * @return {@code true} if member access is allowed */ protected boolean checkAllowlist(Object target, Member member) { + if (!disallowProxyObjectAccess && ProxyUtil.isProxy(target)) { + // If disallowProxyObjectAccess is not set, allow resolving Hibernate entities to their underlying classes/members + // This allows the allowlist capability to function in applications where the developer has accepted this risk + Object newTarget = ProxyUtil.getHibernateProxyTarget(target); + if (newTarget != target) { + target = newTarget; + member = ProxyUtil.resolveTargetMember(member, newTarget); + } + } + Class<?> memberClass = member.getDeclaringClass(); if (!enforceAllowlistEnabled) { return true; diff --git a/core/src/main/java/com/opensymphony/xwork2/util/ProxyUtil.java b/core/src/main/java/com/opensymphony/xwork2/util/ProxyUtil.java index c169af20b..895cfb7ee 100644 --- a/core/src/main/java/com/opensymphony/xwork2/util/ProxyUtil.java +++ b/core/src/main/java/com/opensymphony/xwork2/util/ProxyUtil.java @@ -24,6 +24,7 @@ import com.opensymphony.xwork2.ognl.OgnlCacheFactory; import org.apache.commons.lang3.reflect.ConstructorUtils; import org.apache.commons.lang3.reflect.FieldUtils; import org.apache.commons.lang3.reflect.MethodUtils; +import org.hibernate.Hibernate; import org.hibernate.proxy.HibernateProxy; import java.lang.reflect.Constructor; @@ -33,6 +34,8 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Proxy; +import static java.lang.reflect.Modifier.isPublic; + /** * <code>ProxyUtil</code> * <p> @@ -255,4 +258,34 @@ public class ProxyUtil { return false; } + + /** + * @return the target instance of the given object if it is a Hibernate proxy object, otherwise the given object + */ + public static Object getHibernateProxyTarget(Object object) { + try { + return Hibernate.unproxy(object); + } catch (NoClassDefFoundError ignored) { + return object; + } + } + + /** + * @return matching member on target object if one exists, otherwise the same member + */ + public static Member resolveTargetMember(Member proxyMember, Object target) { + int mod = proxyMember.getModifiers(); + if (proxyMember instanceof Method) { + if (isPublic(mod)) { + return MethodUtils.getMatchingAccessibleMethod(target.getClass(), proxyMember.getName(), ((Method) proxyMember).getParameterTypes()); + } else { + return MethodUtils.getMatchingMethod(target.getClass(), proxyMember.getName(), ((Method) proxyMember).getParameterTypes()); + } + } else if (proxyMember instanceof Field) { + return FieldUtils.getField(target.getClass(), proxyMember.getName(), isPublic(mod)); + } else if (proxyMember instanceof Constructor && isPublic(mod)) { + return ConstructorUtils.getMatchingAccessibleConstructor(target.getClass(), ((Constructor<?>) proxyMember).getParameterTypes()); + } + return proxyMember; + } }