diff --git a/core/src/main/java/com/taobao/arthas/core/command/express/ArthasObjectPropertyAccessor.java b/core/src/main/java/com/taobao/arthas/core/command/express/ArthasObjectPropertyAccessor.java index a8d5ed30a..f83d33044 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/express/ArthasObjectPropertyAccessor.java +++ b/core/src/main/java/com/taobao/arthas/core/command/express/ArthasObjectPropertyAccessor.java @@ -1,21 +1,18 @@ package com.taobao.arthas.core.command.express; -import java.util.Map; - import com.taobao.arthas.core.GlobalOptions; import ognl.ObjectPropertyAccessor; +import ognl.OgnlContext; import ognl.OgnlException; /** - * * @author hengyunabc 2022-03-24 - * */ public class ArthasObjectPropertyAccessor extends ObjectPropertyAccessor { @Override - public Object setPossibleProperty(Map context, Object target, String name, Object value) throws OgnlException { + public Object setPossibleProperty(OgnlContext context, Object target, String name, Object value) throws OgnlException { if (GlobalOptions.strict) { throw new IllegalAccessError(GlobalOptions.STRICT_MESSAGE); } diff --git a/core/src/main/java/com/taobao/arthas/core/command/express/ClassLoaderClassResolver.java b/core/src/main/java/com/taobao/arthas/core/command/express/ClassLoaderClassResolver.java index 6f39ebc3d..e69cf743b 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/express/ClassLoaderClassResolver.java +++ b/core/src/main/java/com/taobao/arthas/core/command/express/ClassLoaderClassResolver.java @@ -4,9 +4,9 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import ognl.ClassResolver; +import ognl.OgnlContext; /** - * * @author hengyunabc 2018-10-18 * @see ognl.DefaultClassResolver */ @@ -20,9 +20,9 @@ public class ClassLoaderClassResolver implements ClassResolver { this.classLoader = classLoader; } + @Override - public Class classForName(String className, @SuppressWarnings("rawtypes") Map context) - throws ClassNotFoundException { + public Class classForName(String className, OgnlContext ognlContext) throws ClassNotFoundException { Class result = null; if ((result = classes.get(className)) == null) { @@ -39,7 +39,6 @@ public class ClassLoaderClassResolver implements ClassResolver { } classes.put(className, result); } - return result; + return (Class) result; } - } diff --git a/core/src/main/java/com/taobao/arthas/core/command/express/CustomClassResolver.java b/core/src/main/java/com/taobao/arthas/core/command/express/CustomClassResolver.java index 7de0e2e95..81c9c174b 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/express/CustomClassResolver.java +++ b/core/src/main/java/com/taobao/arthas/core/command/express/CustomClassResolver.java @@ -1,6 +1,7 @@ package com.taobao.arthas.core.command.express; import ognl.ClassResolver; +import ognl.OgnlContext; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -19,7 +20,8 @@ public class CustomClassResolver implements ClassResolver { } - public Class classForName(String className, Map context) throws ClassNotFoundException { + @Override + public Class classForName(String className, OgnlContext ognlContext) throws ClassNotFoundException { Class result = null; if ((result = classes.get(className)) == null) { @@ -38,6 +40,6 @@ public class CustomClassResolver implements ClassResolver { } classes.put(className, result); } - return result; + return (Class) result; } } diff --git a/core/src/main/java/com/taobao/arthas/core/command/express/DefaultMemberAccess.java b/core/src/main/java/com/taobao/arthas/core/command/express/DefaultMemberAccess.java new file mode 100644 index 000000000..2f5abb4c8 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/express/DefaultMemberAccess.java @@ -0,0 +1,108 @@ +package com.taobao.arthas.core.command.express; + +import ognl.MemberAccess; +import ognl.OgnlContext; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Member; +import java.lang.reflect.Modifier; + +/** + * ognl.DefaultMemberAccess (ognl:ognl:3.1.19) + * + * This class provides methods for setting up and restoring + * access in a Field. Java 2 provides access utilities for setting + * and getting fields that are non-public. This object provides + * coarse-grained access controls to allow access to private, protected + * and package protected members. This will apply to all classes + * and members. + */ +public class DefaultMemberAccess implements MemberAccess { + + public boolean allowPrivateAccess = false; + public boolean allowProtectedAccess = false; + public boolean allowPackageProtectedAccess = false; + + public DefaultMemberAccess(boolean allowAllAccess) { + this(allowAllAccess, allowAllAccess, allowAllAccess); + } + + public DefaultMemberAccess(boolean allowPrivateAccess, boolean allowProtectedAccess, boolean allowPackageProtectedAccess) { + super(); + this.allowPrivateAccess = allowPrivateAccess; + this.allowProtectedAccess = allowProtectedAccess; + this.allowPackageProtectedAccess = allowPackageProtectedAccess; + } + + public boolean getAllowPrivateAccess() { + return allowPrivateAccess; + } + + public void setAllowPrivateAccess(boolean value) { + allowPrivateAccess = value; + } + + public boolean getAllowProtectedAccess() { + return allowProtectedAccess; + } + + public void setAllowProtectedAccess(boolean value) { + allowProtectedAccess = value; + } + + public boolean getAllowPackageProtectedAccess() { + return allowPackageProtectedAccess; + } + + public void setAllowPackageProtectedAccess(boolean value) { + allowPackageProtectedAccess = value; + } + + public Object setup(OgnlContext context, Object target, Member member, String propertyName) { + Object result = null; + + if (isAccessible(context, target, member, propertyName)) { + AccessibleObject accessible = (AccessibleObject) member; + + if (!accessible.isAccessible()) { + result = Boolean.TRUE; + accessible.setAccessible(true); + } + } + return result; + } + + public void restore(OgnlContext context, Object target, Member member, String propertyName, Object state) { + if (state != null) { + ((AccessibleObject)member).setAccessible((Boolean)state); + } + } + + /** + * Returns true if the given member is accessible or can be made accessible + * by this object. + * + * @param context the current execution context (not used). + * @param target the Object to test accessibility for (not used). + * @param member the Member to test accessibility for. + * @param propertyName the property to test accessibility for (not used). + * @return true if the member is accessible in the context, false otherwise. + */ + public boolean isAccessible(OgnlContext context, Object target, Member member, String propertyName) { + int modifiers = member.getModifiers(); + boolean result = Modifier.isPublic(modifiers); + + if (!result) { + if (Modifier.isPrivate(modifiers)) { + result = getAllowPrivateAccess(); + } else { + if (Modifier.isProtected(modifiers)) { + result = getAllowProtectedAccess(); + } else { + result = getAllowPackageProtectedAccess(); + } + } + } + return result; + } +} \ No newline at end of file diff --git a/core/src/main/java/com/taobao/arthas/core/command/express/OgnlExpress.java b/core/src/main/java/com/taobao/arthas/core/command/express/OgnlExpress.java index 490443f7c..f29bd31d2 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/express/OgnlExpress.java +++ b/core/src/main/java/com/taobao/arthas/core/command/express/OgnlExpress.java @@ -4,7 +4,6 @@ import com.alibaba.arthas.deps.org.slf4j.Logger; import com.alibaba.arthas.deps.org.slf4j.LoggerFactory; import ognl.ClassResolver; -import ognl.DefaultMemberAccess; import ognl.MemberAccess; import ognl.Ognl; import ognl.OgnlContext; @@ -28,10 +27,7 @@ public class OgnlExpress implements Express { public OgnlExpress(ClassResolver classResolver) { OgnlRuntime.setPropertyAccessor(Object.class, OBJECT_PROPERTY_ACCESSOR); - context = new OgnlContext(); - context.setClassResolver(classResolver); - // allow private field access - context.setMemberAccess(MEMBER_ACCESS); + context = new OgnlContext(MEMBER_ACCESS, classResolver, null, null); } @Override @@ -65,9 +61,6 @@ public class OgnlExpress implements Express { @Override public Express reset() { context.clear(); - context.setClassResolver(CustomClassResolver.customClassResolver); - // allow private field access - context.setMemberAccess(MEMBER_ACCESS); return this; } } diff --git a/core/src/test/java/com/taobao/arthas/core/command/express/OgnlExpressTest.java b/core/src/test/java/com/taobao/arthas/core/command/express/OgnlExpressTest.java new file mode 100644 index 000000000..05001e214 --- /dev/null +++ b/core/src/test/java/com/taobao/arthas/core/command/express/OgnlExpressTest.java @@ -0,0 +1,53 @@ +package com.taobao.arthas.core.command.express; + +import org.junit.Assert; +import org.junit.Test; + +public class OgnlExpressTest { + + @Test + public void testValidOgnlExpr1() throws ExpressException { + Express unpooledExpress = ExpressFactory.unpooledExpress(OgnlExpressTest.class.getClassLoader()); + Assert.assertEquals(unpooledExpress.get("\"test\".length() % 2 == 0 ? \"even length\" : \"odd length\""), + "even length"); + } + + @Test + public void testValidOgnlExpr2() throws ExpressException { + System.setProperty("ognl.chain.short-circuit", String.valueOf(false)); + Express unpooledExpress = ExpressFactory.unpooledExpress(OgnlExpressTest.class.getClassLoader()); + Assert.assertEquals(unpooledExpress.get("4 in {1, 2, 3, 4}"), true); + Assert.assertEquals(unpooledExpress.get("{1, 2, 3, 4}.{^ #this % 2 == 0}[$]"), 2); + Assert.assertEquals(unpooledExpress.get("{1, 2, 3, 4}.{? #this % 2 == 0}[$]"), 4); + } + + @Test + public void testValidOgnlExpr3() throws ExpressException { + Express unpooledExpress = ExpressFactory.unpooledExpress(OgnlExpressTest.class.getClassLoader()); + Assert.assertEquals(unpooledExpress.get("#factorial = :[#this <= 1 ? 1 : #this * #factorial(#this - 1)], #factorial(5)"), + 120); + } + + @Test + public void testValidOgnlExpr4() throws ExpressException { + Express unpooledExpress = ExpressFactory.unpooledExpress(OgnlExpressTest.class.getClassLoader()); + System.setProperty("arthas.test1", "arthas"); + System.setProperty("arthas.ognl.test2", "test"); + Assert.assertEquals(unpooledExpress.get("#value1=@System@getProperty(\"arthas.test1\")," + + "#value2=@System@getProperty(\"arthas.ognl.test2\"), {#value1, #value2}").toString(), + "[arthas, test]"); + System.clearProperty("arthas.test1"); + System.clearProperty("arthas.ognl.test2"); + } + + @Test + public void testInvalidOgnlExpr() { + try { + Express unpooledExpress = ExpressFactory.unpooledExpress(OgnlExpressTest.class.getClassLoader()); + System.out.println(unpooledExpress.get("#value1=@System.getProperty(\"java.home\")," + + "#value2=@System@getProperty(\"java.runtime.name\"), {#value1, #value2}").toString()); + } catch (Exception e){ + Assert.assertTrue(e.getCause() instanceof ognl.ExpressionSyntaxException); + } + } +} diff --git a/pom.xml b/pom.xml index 2f597428f..602a99a42 100644 --- a/pom.xml +++ b/pom.xml @@ -155,7 +155,7 @@ ognl ognl - 3.1.19 + 3.4.2 junit