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
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;
+
/**
* ProxyUtil
*
@@ -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;
+}
}