Skip to content

Commit eccd236

Browse files
committed
WW-5525 Fix NPE in ProxyUtil for SecurityMemberAccess originating static members
1 parent a1de1cf commit eccd236

File tree

3 files changed

+52
-21
lines changed

3 files changed

+52
-21
lines changed

Diff for: core/src/main/java/org/apache/struts2/ognl/SecurityMemberAccess.java

+10-7
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@
1818
*/
1919
package org.apache.struts2.ognl;
2020

21-
import org.apache.struts2.inject.Inject;
22-
import org.apache.struts2.util.ProxyUtil;
2321
import ognl.MemberAccess;
2422
import org.apache.commons.lang3.BooleanUtils;
2523
import org.apache.logging.log4j.LogManager;
2624
import org.apache.logging.log4j.Logger;
2725
import org.apache.struts2.StrutsConstants;
26+
import org.apache.struts2.inject.Inject;
27+
import org.apache.struts2.util.ProxyUtil;
2828

2929
import java.lang.reflect.AccessibleObject;
3030
import java.lang.reflect.Constructor;
@@ -38,17 +38,17 @@
3838
import java.util.regex.Pattern;
3939
import java.util.stream.IntStream;
4040

41+
import static java.text.MessageFormat.format;
42+
import static java.util.Collections.emptySet;
43+
import static org.apache.struts2.StrutsConstants.STRUTS_ALLOWLIST_CLASSES;
44+
import static org.apache.struts2.StrutsConstants.STRUTS_ALLOWLIST_PACKAGE_NAMES;
4145
import static org.apache.struts2.util.ConfigParseUtil.toClassObjectsSet;
4246
import static org.apache.struts2.util.ConfigParseUtil.toClassesSet;
4347
import static org.apache.struts2.util.ConfigParseUtil.toNewClassesSet;
4448
import static org.apache.struts2.util.ConfigParseUtil.toNewPackageNamesSet;
4549
import static org.apache.struts2.util.ConfigParseUtil.toNewPatternsSet;
4650
import static org.apache.struts2.util.ConfigParseUtil.toPackageNamesSet;
4751
import static org.apache.struts2.util.DebugUtils.logWarningForFirstOccurrence;
48-
import static java.text.MessageFormat.format;
49-
import static java.util.Collections.emptySet;
50-
import static org.apache.struts2.StrutsConstants.STRUTS_ALLOWLIST_CLASSES;
51-
import static org.apache.struts2.StrutsConstants.STRUTS_ALLOWLIST_PACKAGE_NAMES;
5252

5353
/**
5454
* Allows access decisions to be made on the basis of whether a member is static or not.
@@ -141,6 +141,9 @@ public void restore(Map context, Object target, Member member, String propertyNa
141141
public boolean isAccessible(Map context, Object target, Member member, String propertyName) {
142142
LOG.debug("Checking access for [target: {}, member: {}, property: {}]", target, member, propertyName);
143143

144+
if (member == null) {
145+
throw new IllegalArgumentException("Member cannot be null!");
146+
}
144147
if (target != null) {
145148
// Special case: Target is a Class object but not Class.class
146149
if (Class.class.equals(target.getClass()) && !Class.class.equals(target)) {
@@ -209,7 +212,7 @@ protected boolean checkAllowlist(Object target, Member member) {
209212
return true;
210213
}
211214

212-
if (!disallowProxyObjectAccess && target != null && ProxyUtil.isProxy(target)) {
215+
if (!disallowProxyObjectAccess && ProxyUtil.isProxy(target)) {
213216
// If `disallowProxyObjectAccess` is not set, allow resolving Hibernate entities to their underlying
214217
// classes/members. This allows the allowlist capability to continue working and offer some level of
215218
// protection in applications where the developer has accepted the risk of allowing OGNL access to Hibernate

Diff for: core/src/main/java/org/apache/struts2/util/ProxyUtil.java

+5-4
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@
1818
*/
1919
package org.apache.struts2.util;
2020

21-
import org.apache.struts2.ognl.DefaultOgnlCacheFactory;
22-
import org.apache.struts2.ognl.OgnlCache;
23-
import org.apache.struts2.ognl.OgnlCacheFactory;
2421
import org.apache.commons.lang3.reflect.ConstructorUtils;
2522
import org.apache.commons.lang3.reflect.FieldUtils;
2623
import org.apache.commons.lang3.reflect.MethodUtils;
24+
import org.apache.struts2.ognl.DefaultOgnlCacheFactory;
25+
import org.apache.struts2.ognl.OgnlCache;
26+
import org.apache.struts2.ognl.OgnlCacheFactory;
2727
import org.hibernate.Hibernate;
2828
import org.hibernate.proxy.HibernateProxy;
2929

@@ -81,6 +81,7 @@ public static Class<?> ultimateTargetClass(Object candidate) {
8181
* @param object the object to check
8282
*/
8383
public static boolean isProxy(Object object) {
84+
if (object == null) return false;
8485
Class<?> clazz = object.getClass();
8586
Boolean flag = isProxyCache.get(clazz);
8687
if (flag != null) {
@@ -121,7 +122,7 @@ public static boolean isProxyMember(Member member, Object object) {
121122
*/
122123
public static boolean isHibernateProxy(Object object) {
123124
try {
124-
return HibernateProxy.class.isAssignableFrom(object.getClass());
125+
return object != null && HibernateProxy.class.isAssignableFrom(object.getClass());
125126
} catch (NoClassDefFoundError ignored) {
126127
return false;
127128
}

Diff for: core/src/test/java/org/apache/struts2/ognl/OgnlValueStackTest.java

+37-10
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,26 @@
1818
*/
1919
package org.apache.struts2.ognl;
2020

21+
import ognl.OgnlException;
22+
import org.apache.commons.lang3.StringUtils;
23+
import org.apache.logging.log4j.LogManager;
24+
import org.apache.logging.log4j.core.LogEvent;
25+
import org.apache.logging.log4j.core.Logger;
26+
import org.apache.logging.log4j.core.appender.AbstractAppender;
2127
import org.apache.struts2.SimpleAction;
28+
import org.apache.struts2.StrutsConstants;
29+
import org.apache.struts2.StrutsException;
2230
import org.apache.struts2.TestBean;
23-
import org.apache.struts2.text.TextProvider;
2431
import org.apache.struts2.XWorkTestCase;
2532
import org.apache.struts2.config.ConfigurationException;
33+
import org.apache.struts2.config.DefaultPropertiesProvider;
2634
import org.apache.struts2.conversion.impl.ConversionData;
2735
import org.apache.struts2.conversion.impl.XWorkConverter;
2836
import org.apache.struts2.inject.ContainerBuilder;
2937
import org.apache.struts2.ognl.accessor.RootAccessor;
3038
import org.apache.struts2.test.StubConfigurationProvider;
3139
import org.apache.struts2.test.TestBean2;
40+
import org.apache.struts2.text.TextProvider;
3241
import org.apache.struts2.util.Bar;
3342
import org.apache.struts2.util.BarJunior;
3443
import org.apache.struts2.util.Cat;
@@ -37,15 +46,6 @@
3746
import org.apache.struts2.util.ValueStackFactory;
3847
import org.apache.struts2.util.location.LocatableProperties;
3948
import org.apache.struts2.util.reflection.ReflectionContextState;
40-
import ognl.OgnlException;
41-
import org.apache.commons.lang3.StringUtils;
42-
import org.apache.logging.log4j.LogManager;
43-
import org.apache.logging.log4j.core.LogEvent;
44-
import org.apache.logging.log4j.core.Logger;
45-
import org.apache.logging.log4j.core.appender.AbstractAppender;
46-
import org.apache.struts2.StrutsConstants;
47-
import org.apache.struts2.StrutsException;
48-
import org.apache.struts2.config.DefaultPropertiesProvider;
4949

5050
import java.io.ByteArrayInputStream;
5151
import java.io.ByteArrayOutputStream;
@@ -1234,6 +1234,33 @@ public void testOgnlValueStackFromOgnlValueStackFactoryAllStaticAccess() throws
12341234
assertNull("accessed private field (result not null) ?", accessedValue);
12351235
}
12361236

1237+
public void testFindValueWithConstructorAndProxyChecks() {
1238+
loadButSet(Map.of(
1239+
StrutsConstants.STRUTS_DISALLOW_PROXY_OBJECT_ACCESS, Boolean.TRUE.toString(),
1240+
StrutsConstants.STRUTS_DISALLOW_PROXY_MEMBER_ACCESS, Boolean.TRUE.toString()));
1241+
refreshContainerFields();
1242+
1243+
String value = "test";
1244+
String ognlResult = (String) vs.findValue(
1245+
"new org.apache.struts2.ognl.OgnlValueStackTest$ValueHolder('" + value + "').value", String.class);
1246+
1247+
assertEquals(value, ognlResult);
1248+
}
1249+
1250+
@SuppressWarnings({"unused", "ClassCanBeRecord"})
1251+
public static class ValueHolder {
1252+
// See testFindValueWithConstructorAndProxyChecks
1253+
private final String value;
1254+
1255+
public ValueHolder(String value) {
1256+
this.value = value;
1257+
}
1258+
1259+
public String getValue() {
1260+
return value;
1261+
}
1262+
}
1263+
12371264
static class BadJavaBean {
12381265
private int count;
12391266
private int count2;

0 commit comments

Comments
 (0)