Merge pull request #791 from apache/WW-5343-sec-extend

WW-5343 Make SecurityMemberAccess an extensible bean
This commit is contained in:
Kusal Kithul-Godage
2023-11-26 19:36:56 +11:00
committed by GitHub
21 changed files with 529 additions and 826 deletions
@@ -85,6 +85,7 @@ import com.opensymphony.xwork2.ognl.OgnlCacheFactory;
import com.opensymphony.xwork2.ognl.OgnlReflectionProvider;
import com.opensymphony.xwork2.ognl.OgnlUtil;
import com.opensymphony.xwork2.ognl.OgnlValueStackFactory;
import com.opensymphony.xwork2.ognl.SecurityMemberAccess;
import com.opensymphony.xwork2.ognl.accessor.CompoundRootAccessor;
import com.opensymphony.xwork2.util.CompoundRoot;
import com.opensymphony.xwork2.util.OgnlTextParser;
@@ -132,16 +133,13 @@ public class DefaultConfiguration implements Configuration {
static {
Map<String, Object> constants = new HashMap<>();
constants.put(StrutsConstants.STRUTS_DEVMODE, Boolean.FALSE);
constants.put(StrutsConstants.STRUTS_OGNL_LOG_MISSING_PROPERTIES, Boolean.FALSE);
constants.put(StrutsConstants.STRUTS_OGNL_ENABLE_EVAL_EXPRESSION, Boolean.FALSE);
constants.put(StrutsConstants.STRUTS_OGNL_ENABLE_EXPRESSION_CACHE, Boolean.TRUE);
constants.put(StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD, Boolean.FALSE);
constants.put(StrutsConstants.STRUTS_I18N_RELOAD, Boolean.FALSE);
constants.put(StrutsConstants.STRUTS_MATCHER_APPEND_NAMED_PARAMETERS, Boolean.TRUE);
constants.put(StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_TYPE, OgnlCacheFactory.CacheType.BASIC);
constants.put(StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_MAXSIZE, 10000);
constants.put(StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_TYPE, OgnlCacheFactory.CacheType.BASIC);
constants.put(StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_MAXSIZE, 10000);
constants.put(StrutsConstants.STRUTS_ENABLE_DYNAMIC_METHOD_INVOCATION, Boolean.FALSE);
BOOTSTRAP_CONSTANTS = Collections.unmodifiableMap(constants);
}
@@ -385,6 +383,7 @@ public class DefaultConfiguration implements Configuration {
builder.factory(ExpressionCacheFactory.class, DefaultOgnlExpressionCacheFactory.class, Scope.SINGLETON);
builder.factory(BeanInfoCacheFactory.class, DefaultOgnlBeanInfoCacheFactory.class, Scope.SINGLETON);
builder.factory(OgnlUtil.class, Scope.SINGLETON);
builder.factory(SecurityMemberAccess.class, Scope.PROTOTYPE);
builder.factory(OgnlGuard.class, StrutsOgnlGuard.class, Scope.SINGLETON);
builder.factory(ValueSubstitutor.class, EnvsValueSubstitutor.class, Scope.SINGLETON);
@@ -30,7 +30,6 @@ import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.inject.ContainerBuilder;
import com.opensymphony.xwork2.inject.Scope;
import com.opensymphony.xwork2.util.location.LocatableProperties;
import org.apache.struts2.StrutsConstants;
import java.util.HashMap;
import java.util.HashSet;
@@ -62,7 +61,6 @@ public class MockConfiguration implements Configuration {
for (Map.Entry<String, Object> entry : DefaultConfiguration.BOOTSTRAP_CONSTANTS.entrySet()) {
builder.constant(entry.getKey(), String.valueOf(entry.getValue()));
}
builder.constant(StrutsConstants.STRUTS_ENABLE_DYNAMIC_METHOD_INVOCATION, "false");
container = builder.create(true);
}
@@ -75,6 +75,7 @@ import com.opensymphony.xwork2.ognl.OgnlReflectionContextFactory;
import com.opensymphony.xwork2.ognl.OgnlReflectionProvider;
import com.opensymphony.xwork2.ognl.OgnlUtil;
import com.opensymphony.xwork2.ognl.OgnlValueStackFactory;
import com.opensymphony.xwork2.ognl.SecurityMemberAccess;
import com.opensymphony.xwork2.ognl.accessor.CompoundRootAccessor;
import com.opensymphony.xwork2.ognl.accessor.HttpParametersPropertyAccessor;
import com.opensymphony.xwork2.ognl.accessor.ObjectAccessor;
@@ -113,7 +114,6 @@ import com.opensymphony.xwork2.validator.ValidatorFactory;
import com.opensymphony.xwork2.validator.ValidatorFileParser;
import ognl.MethodAccessor;
import ognl.PropertyAccessor;
import org.apache.struts2.StrutsConstants;
import org.apache.struts2.conversion.StrutsConversionPropertiesProcessor;
import org.apache.struts2.conversion.StrutsTypeConverterCreator;
import org.apache.struts2.conversion.StrutsTypeConverterHolder;
@@ -230,6 +230,7 @@ public class StrutsDefaultConfigurationProvider implements ConfigurationProvider
.factory(ExpressionCacheFactory.class, DefaultOgnlExpressionCacheFactory.class, Scope.SINGLETON)
.factory(BeanInfoCacheFactory.class, DefaultOgnlBeanInfoCacheFactory.class, Scope.SINGLETON)
.factory(OgnlUtil.class, Scope.SINGLETON)
.factory(SecurityMemberAccess.class, Scope.PROTOTYPE)
.factory(OgnlGuard.class, StrutsOgnlGuard.class, Scope.SINGLETON)
.factory(CollectionConverter.class, Scope.SINGLETON)
.factory(ArrayConverter.class, Scope.SINGLETON)
@@ -255,8 +256,5 @@ public class StrutsDefaultConfigurationProvider implements ConfigurationProvider
for (Map.Entry<String, Object> entry : DefaultConfiguration.BOOTSTRAP_CONSTANTS.entrySet()) {
props.setProperty(entry.getKey(), String.valueOf(entry.getValue()));
}
props.setProperty(StrutsConstants.STRUTS_ALLOW_STATIC_FIELD_ACCESS, Boolean.TRUE.toString());
props.setProperty(StrutsConstants.STRUTS_ENABLE_DYNAMIC_METHOD_INVOCATION, Boolean.FALSE.toString());
}
}
@@ -18,7 +18,6 @@
*/
package com.opensymphony.xwork2.ognl;
import com.opensymphony.xwork2.config.ConfigurationException;
import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.inject.Inject;
@@ -46,22 +45,16 @@ import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import static com.opensymphony.xwork2.util.TextParseUtil.commaDelimitedStringToSet;
import static com.opensymphony.xwork2.util.ConfigParseUtil.toClassesSet;
import static com.opensymphony.xwork2.util.ConfigParseUtil.toNewPatternsSet;
import static com.opensymphony.xwork2.util.ConfigParseUtil.toPackageNamesSet;
import static java.util.Collections.emptySet;
import static java.util.Collections.unmodifiableSet;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.toSet;
import static org.apache.commons.lang3.StringUtils.strip;
import static org.apache.struts2.StrutsConstants.STRUTS_ALLOWLIST_CLASSES;
import static org.apache.struts2.StrutsConstants.STRUTS_ALLOWLIST_ENABLE;
import static org.apache.struts2.StrutsConstants.STRUTS_ALLOWLIST_PACKAGE_NAMES;
import static org.apache.struts2.ognl.OgnlGuard.EXPR_BLOCKED;
@@ -87,24 +80,12 @@ public class OgnlUtil {
private boolean enableExpressionCache = true;
private boolean enableEvalExpression;
private Set<String> excludedClasses = emptySet();
private Set<Pattern> excludedPackageNamePatterns = emptySet();
private Set<String> excludedPackageNames = emptySet();
private Set<String> excludedPackageExemptClasses = emptySet();
private boolean enforceAllowlistEnabled = false;
private Set<String> allowlistClasses = emptySet();
private Set<String> allowlistPackageNames = emptySet();
private Set<String> devModeExcludedClasses = emptySet();
private Set<Pattern> devModeExcludedPackageNamePatterns = emptySet();
private Set<String> devModeExcludedPackageNames = emptySet();
private Set<String> devModeExcludedPackageExemptClasses = emptySet();
private String devModeExcludedClasses = "";
private String devModeExcludedPackageNamePatterns = "";
private String devModeExcludedPackageNames = "";
private String devModeExcludedPackageExemptClasses = "";
private Container container;
private boolean allowStaticFieldAccess = true;
private boolean disallowProxyMemberAccess = false;
private boolean disallowDefaultPackageAccess = false;
/**
* Construct a new OgnlUtil instance for use with the framework
@@ -145,7 +126,7 @@ public class OgnlUtil {
this.devMode = BooleanUtils.toBoolean(mode);
}
@Inject(StrutsConstants.STRUTS_OGNL_ENABLE_EXPRESSION_CACHE)
@Inject(value = StrutsConstants.STRUTS_OGNL_ENABLE_EXPRESSION_CACHE, required = false)
protected void setEnableExpressionCache(String cache) {
enableExpressionCache = BooleanUtils.toBoolean(cache);
}
@@ -175,146 +156,88 @@ public class OgnlUtil {
}
}
@Inject(value = StrutsConstants.STRUTS_EXCLUDED_CLASSES, required = false)
/**
* @deprecated since 6.4.0, no replacement.
*/
@Deprecated
protected void setExcludedClasses(String commaDelimitedClasses) {
excludedClasses = toNewClassesSet(excludedClasses, commaDelimitedClasses);
// Must be set directly on SecurityMemberAccess
}
@Inject(value = StrutsConstants.STRUTS_DEV_MODE_EXCLUDED_CLASSES, required = false)
protected void setDevModeExcludedClasses(String commaDelimitedClasses) {
devModeExcludedClasses = toNewClassesSet(devModeExcludedClasses, commaDelimitedClasses);
this.devModeExcludedClasses = commaDelimitedClasses;
}
private static Set<String> toClassesSet(String newDelimitedClasses) throws ConfigurationException {
Set<String> classNames = commaDelimitedStringToSet(newDelimitedClasses);
validateClasses(classNames, OgnlUtil.class.getClassLoader());
return unmodifiableSet(classNames);
}
private static Set<String> toNewClassesSet(Set<String> oldClasses, String newDelimitedClasses) throws ConfigurationException {
Set<String> classNames = commaDelimitedStringToSet(newDelimitedClasses);
validateClasses(classNames, OgnlUtil.class.getClassLoader());
Set<String> excludedClasses = new HashSet<>(oldClasses);
excludedClasses.addAll(classNames);
return unmodifiableSet(excludedClasses);
}
private static void validateClasses(Set<String> classNames, ClassLoader validatingClassLoader) throws ConfigurationException {
for (String className : classNames) {
try {
validatingClassLoader.loadClass(className);
} catch (ClassNotFoundException e) {
throw new ConfigurationException("Cannot load class for exclusion/exemption configuration: " + className, e);
}
}
}
@Inject(value = StrutsConstants.STRUTS_EXCLUDED_PACKAGE_NAME_PATTERNS, required = false)
/**
* @deprecated since 6.4.0, no replacement.
*/
@Deprecated
protected void setExcludedPackageNamePatterns(String commaDelimitedPackagePatterns) {
excludedPackageNamePatterns = toNewPatternsSet(excludedPackageNamePatterns, commaDelimitedPackagePatterns);
// Must be set directly on SecurityMemberAccess
}
@Inject(value = StrutsConstants.STRUTS_DEV_MODE_EXCLUDED_PACKAGE_NAME_PATTERNS, required = false)
protected void setDevModeExcludedPackageNamePatterns(String commaDelimitedPackagePatterns) {
devModeExcludedPackageNamePatterns = toNewPatternsSet(devModeExcludedPackageNamePatterns, commaDelimitedPackagePatterns);
this.devModeExcludedPackageNamePatterns = commaDelimitedPackagePatterns;
}
private static Set<Pattern> toNewPatternsSet(Set<Pattern> oldPatterns, String newDelimitedPatterns) throws ConfigurationException {
Set<String> patterns = commaDelimitedStringToSet(newDelimitedPatterns);
Set<Pattern> newPatterns = new HashSet<>(oldPatterns);
for (String pattern: patterns) {
try {
newPatterns.add(Pattern.compile(pattern));
} catch (PatternSyntaxException e) {
throw new ConfigurationException("Excluded package name patterns could not be parsed due to invalid regex: " + pattern, e);
}
}
return unmodifiableSet(newPatterns);
}
@Inject(value = StrutsConstants.STRUTS_EXCLUDED_PACKAGE_NAMES, required = false)
/**
* @deprecated since 6.4.0, no replacement.
*/
@Deprecated
protected void setExcludedPackageNames(String commaDelimitedPackageNames) {
excludedPackageNames = toNewPackageNamesSet(excludedPackageNames, commaDelimitedPackageNames);
// Must be set directly on SecurityMemberAccess
}
@Inject(value = StrutsConstants.STRUTS_DEV_MODE_EXCLUDED_PACKAGE_NAMES, required = false)
protected void setDevModeExcludedPackageNames(String commaDelimitedPackageNames) {
devModeExcludedPackageNames = toNewPackageNamesSet(devModeExcludedPackageNames, commaDelimitedPackageNames);
this.devModeExcludedPackageNames = commaDelimitedPackageNames;
}
private static Set<String> toPackageNamesSet(String newDelimitedPackageNames) throws ConfigurationException {
Set<String> packageNames = commaDelimitedStringToSet(newDelimitedPackageNames)
.stream().map(s -> strip(s, ".")).collect(toSet());
validatePackageNames(packageNames);
return unmodifiableSet(packageNames);
}
private static Set<String> toNewPackageNamesSet(Collection<String> oldPackageNames, String newDelimitedPackageNames) throws ConfigurationException {
Set<String> packageNames = commaDelimitedStringToSet(newDelimitedPackageNames)
.stream().map(s -> strip(s, ".")).collect(toSet());
validatePackageNames(packageNames);
Set<String> newPackageNames = new HashSet<>(oldPackageNames);
newPackageNames.addAll(packageNames);
return unmodifiableSet(newPackageNames);
}
private static void validatePackageNames(Collection<String> packageNames) {
if (packageNames.stream().anyMatch(s -> Pattern.compile("\\s").matcher(s).find())) {
throw new ConfigurationException("Excluded package names could not be parsed due to erroneous whitespace characters: " + packageNames);
}
}
@Inject(value = StrutsConstants.STRUTS_EXCLUDED_PACKAGE_EXEMPT_CLASSES, required = false)
/**
* @deprecated since 6.4.0, no replacement.
*/
@Deprecated
public void setExcludedPackageExemptClasses(String commaDelimitedClasses) {
excludedPackageExemptClasses = toClassesSet(commaDelimitedClasses);
// Must be set directly on SecurityMemberAccess
}
@Inject(value = StrutsConstants.STRUTS_DEV_MODE_EXCLUDED_PACKAGE_EXEMPT_CLASSES, required = false)
public void setDevModeExcludedPackageExemptClasses(String commaDelimitedClasses) {
devModeExcludedPackageExemptClasses = toClassesSet(commaDelimitedClasses);
this.devModeExcludedPackageExemptClasses = commaDelimitedClasses;
}
/**
* @deprecated since 6.4.0, no replacement.
*/
@Deprecated
public Set<String> getExcludedClasses() {
return excludedClasses;
return toClassesSet(container.getInstance(String.class, StrutsConstants.STRUTS_EXCLUDED_CLASSES));
}
/**
* @deprecated since 6.4.0, no replacement.
*/
@Deprecated
public Set<Pattern> getExcludedPackageNamePatterns() {
return excludedPackageNamePatterns;
return toNewPatternsSet(emptySet(), container.getInstance(String.class, StrutsConstants.STRUTS_EXCLUDED_PACKAGE_NAME_PATTERNS));
}
/**
* @deprecated since 6.4.0, no replacement.
*/
@Deprecated
public Set<String> getExcludedPackageNames() {
return excludedPackageNames;
return toPackageNamesSet(container.getInstance(String.class, StrutsConstants.STRUTS_EXCLUDED_PACKAGE_NAMES));
}
/**
* @deprecated since 6.4.0, no replacement.
*/
@Deprecated
public Set<String> getExcludedPackageExemptClasses() {
return excludedPackageExemptClasses;
}
@Inject(value = STRUTS_ALLOWLIST_ENABLE, required = false)
protected void setEnforceAllowlistEnabled(String enforceAllowlistEnabled) {
this.enforceAllowlistEnabled = BooleanUtils.toBoolean(enforceAllowlistEnabled);
}
@Inject(value = STRUTS_ALLOWLIST_CLASSES, required = false)
protected void setAllowlistClasses(String commaDelimitedClasses) {
allowlistClasses = toClassesSet(commaDelimitedClasses);
}
@Inject(value = STRUTS_ALLOWLIST_PACKAGE_NAMES, required = false)
protected void setAllowlistPackageNames(String commaDelimitedPackageNames) {
allowlistPackageNames = toPackageNamesSet(commaDelimitedPackageNames);
}
public boolean isEnforceAllowlistEnabled() {
return enforceAllowlistEnabled;
}
public Set<String> getAllowlistClasses() {
return allowlistClasses;
}
public Set<String> getAllowlistPackageNames() {
return allowlistPackageNames;
return toClassesSet(container.getInstance(String.class, StrutsConstants.STRUTS_EXCLUDED_PACKAGE_EXEMPT_CLASSES));
}
@Inject
@@ -322,19 +245,28 @@ public class OgnlUtil {
this.container = container;
}
@Inject(value = StrutsConstants.STRUTS_ALLOW_STATIC_FIELD_ACCESS, required = false)
/**
* @deprecated since 6.4.0, no replacement.
*/
@Deprecated
protected void setAllowStaticFieldAccess(String allowStaticFieldAccess) {
this.allowStaticFieldAccess = BooleanUtils.toBoolean(allowStaticFieldAccess);
// Must be set directly on SecurityMemberAccess
}
@Inject(value = StrutsConstants.STRUTS_DISALLOW_PROXY_MEMBER_ACCESS, required = false)
/**
* @deprecated since 6.4.0, no replacement.
*/
@Deprecated
protected void setDisallowProxyMemberAccess(String disallowProxyMemberAccess) {
this.disallowProxyMemberAccess = BooleanUtils.toBoolean(disallowProxyMemberAccess);
// Must be set directly on SecurityMemberAccess
}
@Inject(value = StrutsConstants.STRUTS_DISALLOW_DEFAULT_PACKAGE_ACCESS, required = false)
/**
* @deprecated since 6.4.0, no replacement.
*/
@Deprecated
protected void setDisallowDefaultPackageAccess(String disallowDefaultPackageAccess) {
this.disallowDefaultPackageAccess = BooleanUtils.toBoolean(disallowDefaultPackageAccess);
// Must be set directly on SecurityMemberAccess
}
/**
@@ -356,12 +288,20 @@ public class OgnlUtil {
}
}
/**
* @deprecated since 6.4.0, no replacement.
*/
@Deprecated
public boolean isDisallowProxyMemberAccess() {
return disallowProxyMemberAccess;
return BooleanUtils.toBoolean(container.getInstance(String.class, StrutsConstants.STRUTS_DISALLOW_PROXY_MEMBER_ACCESS));
}
/**
* @deprecated since 6.4.0, no replacement.
*/
@Deprecated
public boolean isDisallowDefaultPackageAccess() {
return disallowDefaultPackageAccess;
return BooleanUtils.toBoolean(container.getInstance(String.class, StrutsConstants.STRUTS_DISALLOW_DEFAULT_PACKAGE_ACCESS));
}
/**
@@ -922,8 +862,7 @@ public class OgnlUtil {
resolver = container.getInstance(CompoundRootAccessor.class);
}
SecurityMemberAccess memberAccess = new SecurityMemberAccess(allowStaticFieldAccess);
memberAccess.disallowProxyMemberAccess(disallowProxyMemberAccess);
SecurityMemberAccess memberAccess = container.getInstance(SecurityMemberAccess.class);
if (devMode) {
if (!warnReported.get()) {
@@ -934,14 +873,6 @@ public class OgnlUtil {
memberAccess.useExcludedPackageNamePatterns(devModeExcludedPackageNamePatterns);
memberAccess.useExcludedPackageNames(devModeExcludedPackageNames);
memberAccess.useExcludedPackageExemptClasses(devModeExcludedPackageExemptClasses);
} else {
memberAccess.useExcludedClasses(getExcludedClasses());
memberAccess.useExcludedPackageNamePatterns(getExcludedPackageNamePatterns());
memberAccess.useExcludedPackageNames(getExcludedPackageNames());
memberAccess.useExcludedPackageExemptClasses(getExcludedPackageExemptClasses());
memberAccess.useEnforceAllowlistEnabled(isEnforceAllowlistEnabled());
memberAccess.useAllowlistClasses(getAllowlistClasses());
memberAccess.useAllowlistPackageNames(getAllowlistPackageNames());
}
return Ognl.createDefaultContext(root, memberAccess, resolver, defaultConverter);
@@ -77,38 +77,91 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
private boolean devMode;
private boolean logMissingProperties;
protected OgnlValueStack(XWorkConverter xworkConverter, CompoundRootAccessor accessor, TextProvider prov, boolean allowStaticFieldAccess) {
setRoot(xworkConverter, accessor, new CompoundRoot(), allowStaticFieldAccess);
push(prov);
/**
* @since 6.4.0
*/
protected OgnlValueStack(ValueStack vs,
XWorkConverter xworkConverter,
CompoundRootAccessor accessor,
TextProvider prov,
SecurityMemberAccess securityMemberAccess) {
setRoot(xworkConverter,
accessor,
vs != null ? new CompoundRoot(vs.getRoot()) : new CompoundRoot(),
securityMemberAccess);
if (prov != null) {
push(prov);
}
}
/**
* @since 6.4.0
*/
protected OgnlValueStack(XWorkConverter xworkConverter, CompoundRootAccessor accessor, TextProvider prov, SecurityMemberAccess securityMemberAccess) {
this(null, xworkConverter, accessor, prov, securityMemberAccess);
}
/**
* @since 6.4.0
*/
protected OgnlValueStack(ValueStack vs, XWorkConverter xworkConverter, CompoundRootAccessor accessor, SecurityMemberAccess securityMemberAccess) {
this(vs, xworkConverter, accessor, null, securityMemberAccess);
}
/**
* @deprecated since 6.4.0, use {@link #OgnlValueStack(ValueStack, XWorkConverter, CompoundRootAccessor, TextProvider, SecurityMemberAccess)} instead.
*/
@Deprecated
protected OgnlValueStack(ValueStack vs,
XWorkConverter xworkConverter,
CompoundRootAccessor accessor,
TextProvider prov,
boolean allowStaticFieldAccess) {
this(vs, xworkConverter, accessor, prov, new SecurityMemberAccess(allowStaticFieldAccess));
}
/**
* @deprecated since 6.4.0, use {@link #OgnlValueStack(XWorkConverter, CompoundRootAccessor, TextProvider, SecurityMemberAccess)} instead.
*/
@Deprecated
protected OgnlValueStack(XWorkConverter xworkConverter, CompoundRootAccessor accessor, TextProvider prov, boolean allowStaticFieldAccess) {
this(xworkConverter, accessor, prov, new SecurityMemberAccess(allowStaticFieldAccess));
}
/**
* @deprecated since 6.4.0, use {@link #OgnlValueStack(ValueStack, XWorkConverter, CompoundRootAccessor, SecurityMemberAccess)} instead.
*/
@Deprecated
protected OgnlValueStack(ValueStack vs, XWorkConverter xworkConverter, CompoundRootAccessor accessor, boolean allowStaticFieldAccess) {
setRoot(xworkConverter, accessor, new CompoundRoot(vs.getRoot()), allowStaticFieldAccess);
this(vs, xworkConverter, accessor, new SecurityMemberAccess(allowStaticFieldAccess));
}
@Inject
protected void setOgnlUtil(OgnlUtil ognlUtil) {
this.ognlUtil = ognlUtil;
securityMemberAccess.useExcludedClasses(ognlUtil.getExcludedClasses());
securityMemberAccess.useExcludedPackageNamePatterns(ognlUtil.getExcludedPackageNamePatterns());
securityMemberAccess.useExcludedPackageNames(ognlUtil.getExcludedPackageNames());
securityMemberAccess.useExcludedPackageExemptClasses(ognlUtil.getExcludedPackageExemptClasses());
securityMemberAccess.useEnforceAllowlistEnabled(ognlUtil.isEnforceAllowlistEnabled());
securityMemberAccess.useAllowlistClasses(ognlUtil.getAllowlistClasses());
securityMemberAccess.useAllowlistPackageNames(ognlUtil.getAllowlistPackageNames());
securityMemberAccess.disallowProxyMemberAccess(ognlUtil.isDisallowProxyMemberAccess());
securityMemberAccess.disallowDefaultPackageAccess(ognlUtil.isDisallowDefaultPackageAccess());
}
protected void setRoot(XWorkConverter xworkConverter, CompoundRootAccessor accessor, CompoundRoot compoundRoot, boolean allowStaticFieldAccess) {
/**
* @since 6.4.0
*/
protected void setRoot(XWorkConverter xworkConverter, CompoundRootAccessor accessor, CompoundRoot compoundRoot, SecurityMemberAccess securityMemberAccess) {
this.root = compoundRoot;
this.securityMemberAccess = new SecurityMemberAccess(allowStaticFieldAccess);
this.securityMemberAccess = securityMemberAccess;
this.context = Ognl.createDefaultContext(this.root, securityMemberAccess, accessor, new OgnlTypeConverterWrapper(xworkConverter));
this.converter = xworkConverter;
context.put(VALUE_STACK, this);
((OgnlContext) context).setTraceEvaluations(false);
((OgnlContext) context).setKeepLastEvaluation(false);
}
/**
* @deprecated since 6.4.0, use {@link #setRoot(XWorkConverter, CompoundRootAccessor, CompoundRoot, SecurityMemberAccess)} instead.
*/
@Deprecated
protected void setRoot(XWorkConverter xworkConverter, CompoundRootAccessor accessor, CompoundRoot compoundRoot, boolean allowStaticFieldAccess) {
setRoot(xworkConverter, accessor, compoundRoot, new SecurityMemberAccess(allowStaticFieldAccess));
}
@Inject(StrutsConstants.STRUTS_DEVMODE)
protected void setDevMode(String mode) {
this.devMode = BooleanUtils.toBoolean(mode);
@@ -464,6 +517,9 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
return root.size();
}
/**
* Retained for serializability - see {@link com.opensymphony.xwork2.ognl.OgnlValueStackTest#testSerializable}
*/
private Object readResolve() {
// TODO: this should be done better
ActionContext ac = ActionContext.getContext();
@@ -471,41 +527,32 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
XWorkConverter xworkConverter = cont.getInstance(XWorkConverter.class);
CompoundRootAccessor accessor = (CompoundRootAccessor) cont.getInstance(PropertyAccessor.class, CompoundRoot.class.getName());
TextProvider prov = cont.getInstance(TextProvider.class, "system");
final boolean allowStaticField = BooleanUtils.toBoolean(cont.getInstance(String.class, StrutsConstants.STRUTS_ALLOW_STATIC_FIELD_ACCESS));
OgnlValueStack aStack = new OgnlValueStack(xworkConverter, accessor, prov, allowStaticField);
SecurityMemberAccess sma = cont.getInstance(SecurityMemberAccess.class);
OgnlValueStack aStack = new OgnlValueStack(xworkConverter, accessor, prov, sma);
aStack.setOgnlUtil(cont.getInstance(OgnlUtil.class));
aStack.setRoot(xworkConverter, accessor, this.root, allowStaticField);
aStack.setRoot(xworkConverter, accessor, this.root, sma);
return aStack;
}
public void clearContextValues() {
//this is an OGNL ValueStack so the context will be an OgnlContext
//it would be better to make context of type OgnlContext
((OgnlContext) context).getValues().clear();
}
@Deprecated
public void setAcceptProperties(Set<Pattern> acceptedProperties) {
securityMemberAccess.useAcceptProperties(acceptedProperties);
}
public void useAcceptProperties(Set<Pattern> acceptedProperties) {
securityMemberAccess.useAcceptProperties(acceptedProperties);
}
@Deprecated
public void setExcludeProperties(Set<Pattern> excludeProperties) {
securityMemberAccess.useExcludeProperties(excludeProperties);
}
public void useExcludeProperties(Set<Pattern> excludeProperties) {
securityMemberAccess.useExcludeProperties(excludeProperties);
}
@Inject
/**
* @deprecated since 6.4.0, no replacement.
*/
@Deprecated
protected void setXWorkConverter(final XWorkConverter converter) {
this.converter = converter;
// no-op
}
}
@@ -31,11 +31,8 @@ import ognl.MethodAccessor;
import ognl.OgnlRuntime;
import ognl.PropertyAccessor;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.StrutsConstants;
import java.util.Map;
import java.util.Set;
/**
@@ -59,21 +56,17 @@ public class OgnlValueStackFactory implements ValueStackFactory {
}
public ValueStack createValueStack() {
ValueStack stack = new OgnlValueStack(xworkConverter, compoundRootAccessor, textProvider, containerAllowsStaticFieldAccess());
ValueStack stack = new OgnlValueStack(
xworkConverter, compoundRootAccessor, textProvider, container.getInstance(SecurityMemberAccess.class));
container.inject(stack);
return stack.getActionContext()
.withContainer(container)
.withValueStack(stack)
.getValueStack();
return stack.getActionContext().withContainer(container).withValueStack(stack).getValueStack();
}
public ValueStack createValueStack(ValueStack stack) {
ValueStack result = new OgnlValueStack(stack, xworkConverter, compoundRootAccessor, containerAllowsStaticFieldAccess());
ValueStack result = new OgnlValueStack(
stack, xworkConverter, compoundRootAccessor, container.getInstance(SecurityMemberAccess.class));
container.inject(result);
return result.getActionContext()
.withContainer(container)
.withValueStack(result)
.getValueStack();
return result.getActionContext().withContainer(container).withValueStack(result).getValueStack();
}
@Inject
@@ -105,10 +98,10 @@ public class OgnlValueStackFactory implements ValueStackFactory {
}
/**
* Retrieve allowStaticFieldAccess state from the container (allows for lazy fetching)
* @deprecated since 6.4.0, no replacement.
*/
@Deprecated
protected boolean containerAllowsStaticFieldAccess() {
return BooleanUtils.toBoolean(container.getInstance(String.class, StrutsConstants.STRUTS_ALLOW_STATIC_FIELD_ACCESS));
}
}
@@ -18,10 +18,13 @@
*/
package com.opensymphony.xwork2.ognl;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.ProxyUtil;
import ognl.MemberAccess;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.StrutsConstants;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
@@ -36,10 +39,15 @@ import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static com.opensymphony.xwork2.util.ConfigParseUtil.toClassesSet;
import static com.opensymphony.xwork2.util.ConfigParseUtil.toNewClassesSet;
import static com.opensymphony.xwork2.util.ConfigParseUtil.toNewPackageNamesSet;
import static com.opensymphony.xwork2.util.ConfigParseUtil.toNewPatternsSet;
import static com.opensymphony.xwork2.util.ConfigParseUtil.toPackageNamesSet;
import static java.text.MessageFormat.format;
import static java.util.Collections.emptySet;
import static java.util.Collections.singletonList;
import static java.util.Collections.unmodifiableSet;
import static java.util.stream.Collectors.toSet;
/**
* Allows access decisions to be made on the basis of whether a member is static or not.
@@ -49,10 +57,10 @@ public class SecurityMemberAccess implements MemberAccess {
private static final Logger LOG = LogManager.getLogger(SecurityMemberAccess.class);
private final boolean allowStaticFieldAccess;
private boolean allowStaticFieldAccess = true;
private Set<Pattern> excludeProperties = emptySet();
private Set<Pattern> acceptProperties = emptySet();
private Set<String> excludedClasses = emptySet();
private Set<String> excludedClasses = unmodifiableSet(new HashSet<>(singletonList(Object.class.getName())));
private Set<Pattern> excludedPackageNamePatterns = emptySet();
private Set<String> excludedPackageNames = emptySet();
private Set<String> excludedPackageExemptClasses = emptySet();
@@ -62,16 +70,20 @@ public class SecurityMemberAccess implements MemberAccess {
private boolean disallowProxyMemberAccess = false;
private boolean disallowDefaultPackageAccess = false;
public SecurityMemberAccess() {
}
/**
* SecurityMemberAccess
* - access decisions based on whether member is static (or not)
* - block or allow access to properties (configurable-after-construction)
*
* @param allowStaticFieldAccess if set to true static fields (constants) will be accessible
* @deprecated since 6.4.0, use {@link #SecurityMemberAccess()} instead.
*/
@Deprecated
public SecurityMemberAccess(boolean allowStaticFieldAccess) {
this.allowStaticFieldAccess = allowStaticFieldAccess;
useExcludedClasses(excludedClasses); // Initialise default exclusions
useAllowStaticFieldAccess(String.valueOf(allowStaticFieldAccess));
}
@Override
@@ -356,108 +368,64 @@ public class SecurityMemberAccess implements MemberAccess {
return excludeProperties.stream().map(pattern -> pattern.matcher(paramName)).anyMatch(Matcher::matches);
}
/**
* @deprecated please use {@link #useExcludeProperties(Set)}
*/
@Deprecated
public void setExcludeProperties(Set<Pattern> excludeProperties) {
this.excludeProperties = excludeProperties;
}
public void useExcludeProperties(Set<Pattern> excludeProperties) {
this.excludeProperties = excludeProperties;
}
/**
* @deprecated please use {@link #useAcceptProperties(Set)}
*/
@Deprecated
public void setAcceptProperties(Set<Pattern> acceptedProperties) {
this.acceptProperties = acceptedProperties;
}
public void useAcceptProperties(Set<Pattern> acceptedProperties) {
this.acceptProperties = acceptedProperties;
}
/**
* @deprecated please use {@link #useExcludedClasses(Set)}
*/
@Deprecated
public void setExcludedClasses(Set<Class<?>> excludedClasses) {
useExcludedClasses(excludedClasses.stream().map(Class::getName).collect(toSet()));
}
public void useExcludedClasses(Set<String> excludedClasses) {
Set<String> newExcludedClasses = new HashSet<>(excludedClasses);
newExcludedClasses.add(Object.class.getName());
if (!allowStaticFieldAccess) {
newExcludedClasses.add(Class.class.getName());
@Inject(value = StrutsConstants.STRUTS_ALLOW_STATIC_FIELD_ACCESS, required = false)
public void useAllowStaticFieldAccess(String allowStaticFieldAccess) {
this.allowStaticFieldAccess = BooleanUtils.toBoolean(allowStaticFieldAccess);
if (!this.allowStaticFieldAccess) {
useExcludedClasses(Class.class.getName());
}
this.excludedClasses = unmodifiableSet(newExcludedClasses);
}
/**
* @deprecated please use {@link #useExcludedPackageNamePatterns(Set)}
*/
@Deprecated
public void setExcludedPackageNamePatterns(Set<Pattern> excludedPackageNamePatterns) {
this.excludedPackageNamePatterns = excludedPackageNamePatterns;
@Inject(value = StrutsConstants.STRUTS_EXCLUDED_CLASSES, required = false)
public void useExcludedClasses(String commaDelimitedClasses) {
this.excludedClasses = toNewClassesSet(excludedClasses, commaDelimitedClasses);
}
public void useExcludedPackageNamePatterns(Set<Pattern> excludedPackageNamePatterns) {
this.excludedPackageNamePatterns = excludedPackageNamePatterns;
@Inject(value = StrutsConstants.STRUTS_EXCLUDED_PACKAGE_NAME_PATTERNS, required = false)
public void useExcludedPackageNamePatterns(String commaDelimitedPackagePatterns) {
this.excludedPackageNamePatterns = toNewPatternsSet(excludedPackageNamePatterns, commaDelimitedPackagePatterns);
}
/**
* @deprecated please use {@link #useExcludedPackageNames(Set)}
*/
@Deprecated
public void setExcludedPackageNames(Set<String> excludedPackageNames) {
this.excludedPackageNames = excludedPackageNames;
@Inject(value = StrutsConstants.STRUTS_EXCLUDED_PACKAGE_NAMES, required = false)
public void useExcludedPackageNames(String commaDelimitedPackageNames) {
this.excludedPackageNames = toNewPackageNamesSet(excludedPackageNames, commaDelimitedPackageNames);
}
public void useExcludedPackageNames(Set<String> excludedPackageNames) {
this.excludedPackageNames = excludedPackageNames;
@Inject(value = StrutsConstants.STRUTS_EXCLUDED_PACKAGE_EXEMPT_CLASSES, required = false)
public void useExcludedPackageExemptClasses(String commaDelimitedClasses) {
this.excludedPackageExemptClasses = toClassesSet(commaDelimitedClasses);
}
/**
* @deprecated please use {@link #useExcludedPackageExemptClasses(Set)}
*/
@Deprecated
public void setExcludedPackageExemptClasses(Set<Class<?>> excludedPackageExemptClasses) {
useExcludedPackageExemptClasses(excludedPackageExemptClasses.stream().map(Class::getName).collect(toSet()));
@Inject(value = StrutsConstants.STRUTS_ALLOWLIST_ENABLE, required = false)
public void useEnforceAllowlistEnabled(String enforceAllowlistEnabled) {
this.enforceAllowlistEnabled = BooleanUtils.toBoolean(enforceAllowlistEnabled);
}
public void useExcludedPackageExemptClasses(Set<String> excludedPackageExemptClasses) {
this.excludedPackageExemptClasses = excludedPackageExemptClasses;
@Inject(value = StrutsConstants.STRUTS_ALLOWLIST_CLASSES, required = false)
public void useAllowlistClasses(String commaDelimitedClasses) {
this.allowlistClasses = toClassesSet(commaDelimitedClasses);
}
public void useEnforceAllowlistEnabled(boolean enforceAllowlistEnabled) {
this.enforceAllowlistEnabled = enforceAllowlistEnabled;
@Inject(value = StrutsConstants.STRUTS_ALLOWLIST_PACKAGE_NAMES, required = false)
public void useAllowlistPackageNames(String commaDelimitedPackageNames) {
this.allowlistPackageNames = toPackageNamesSet(commaDelimitedPackageNames);
}
public void useAllowlistClasses(Set<String> allowlistClasses) {
this.allowlistClasses = allowlistClasses;
@Inject(value = StrutsConstants.STRUTS_DISALLOW_PROXY_MEMBER_ACCESS, required = false)
public void useDisallowProxyMemberAccess(String disallowProxyMemberAccess) {
this.disallowProxyMemberAccess = BooleanUtils.toBoolean(disallowProxyMemberAccess);
}
public void useAllowlistPackageNames(Set<String> allowlistPackageNames) {
this.allowlistPackageNames = allowlistPackageNames;
}
/**
* @deprecated please use {@link #disallowProxyMemberAccess(boolean)}
*/
@Deprecated
public void setDisallowProxyMemberAccess(boolean disallowProxyMemberAccess) {
this.disallowProxyMemberAccess = disallowProxyMemberAccess;
}
public void disallowProxyMemberAccess(boolean disallowProxyMemberAccess) {
this.disallowProxyMemberAccess = disallowProxyMemberAccess;
}
public void disallowDefaultPackageAccess(boolean disallowDefaultPackageAccess) {
this.disallowDefaultPackageAccess = disallowDefaultPackageAccess;
@Inject(value = StrutsConstants.STRUTS_DISALLOW_DEFAULT_PACKAGE_ACCESS, required = false)
public void useDisallowDefaultPackageAccess(String disallowDefaultPackageAccess) {
this.disallowDefaultPackageAccess = BooleanUtils.toBoolean(disallowDefaultPackageAccess);
}
}
@@ -0,0 +1,98 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.opensymphony.xwork2.util;
import com.opensymphony.xwork2.config.ConfigurationException;
import com.opensymphony.xwork2.ognl.OgnlUtil;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import static com.opensymphony.xwork2.util.TextParseUtil.commaDelimitedStringToSet;
import static java.util.Collections.unmodifiableSet;
import static java.util.stream.Collectors.toSet;
import static org.apache.commons.lang3.StringUtils.strip;
public class ConfigParseUtil {
private ConfigParseUtil() {
}
public static Set<String> toClassesSet(String newDelimitedClasses) throws ConfigurationException {
Set<String> classNames = commaDelimitedStringToSet(newDelimitedClasses);
validateClasses(classNames, OgnlUtil.class.getClassLoader());
return unmodifiableSet(classNames);
}
public static Set<String> toNewClassesSet(Set<String> oldClasses, String newDelimitedClasses) throws ConfigurationException {
Set<String> classNames = commaDelimitedStringToSet(newDelimitedClasses);
validateClasses(classNames, OgnlUtil.class.getClassLoader());
Set<String> excludedClasses = new HashSet<>(oldClasses);
excludedClasses.addAll(classNames);
return unmodifiableSet(excludedClasses);
}
public static Set<Pattern> toNewPatternsSet(Set<Pattern> oldPatterns, String newDelimitedPatterns) throws ConfigurationException {
Set<String> patterns = commaDelimitedStringToSet(newDelimitedPatterns);
Set<Pattern> newPatterns = new HashSet<>(oldPatterns);
for (String pattern: patterns) {
try {
newPatterns.add(Pattern.compile(pattern));
} catch (PatternSyntaxException e) {
throw new ConfigurationException("Excluded package name patterns could not be parsed due to invalid regex: " + pattern, e);
}
}
return unmodifiableSet(newPatterns);
}
public static void validateClasses(Set<String> classNames, ClassLoader validatingClassLoader) throws ConfigurationException {
for (String className : classNames) {
try {
validatingClassLoader.loadClass(className);
} catch (ClassNotFoundException e) {
throw new ConfigurationException("Cannot load class for exclusion/exemption configuration: " + className, e);
}
}
}
public static Set<String> toPackageNamesSet(String newDelimitedPackageNames) throws ConfigurationException {
Set<String> packageNames = commaDelimitedStringToSet(newDelimitedPackageNames)
.stream().map(s -> strip(s, ".")).collect(toSet());
validatePackageNames(packageNames);
return unmodifiableSet(packageNames);
}
public static Set<String> toNewPackageNamesSet(Collection<String> oldPackageNames, String newDelimitedPackageNames) throws ConfigurationException {
Set<String> packageNames = commaDelimitedStringToSet(newDelimitedPackageNames)
.stream().map(s -> strip(s, ".")).collect(toSet());
validatePackageNames(packageNames);
Set<String> newPackageNames = new HashSet<>(oldPackageNames);
newPackageNames.addAll(packageNames);
return unmodifiableSet(newPackageNames);
}
public static void validatePackageNames(Collection<String> packageNames) {
if (packageNames.stream().anyMatch(s -> Pattern.compile("\\s").matcher(s).find())) {
throw new ConfigurationException("Excluded package names could not be parsed due to erroneous whitespace characters: " + packageNames);
}
}
}
@@ -31,7 +31,9 @@ public interface MemberAccessValueStack {
* @deprecated please use {@link #useExcludeProperties(Set)}
*/
@Deprecated
void setExcludeProperties(Set<Pattern> excludeProperties);
default void setExcludeProperties(Set<Pattern> excludeProperties) {
useExcludeProperties(excludeProperties);
}
void useExcludeProperties(Set<Pattern> excludeProperties);
@@ -39,7 +41,9 @@ public interface MemberAccessValueStack {
* @deprecated please use {@link #useAcceptProperties(Set)}
*/
@Deprecated
void setAcceptProperties(Set<Pattern> acceptedProperties);
default void setAcceptProperties(Set<Pattern> acceptedProperties) {
useAcceptProperties(acceptedProperties);
}
void useAcceptProperties(Set<Pattern> acceptedProperties);
@@ -37,7 +37,7 @@ public class LocationImpl implements Location, Serializable {
private final int line;
private final int column;
private final String description;
// Package private: outside this package, use Location.UNKNOWN.
static final LocationImpl UNKNOWN = new LocationImpl(null, null, -1, -1);
@@ -71,16 +71,16 @@ public class LocationImpl implements Location, Serializable {
}
this.description = StringUtils.trimToNull(description);
}
/**
* Copy constructor.
*
*
* @param location the location to be copied
*/
public LocationImpl(Location location) {
this(location.getDescription(), location.getURI(), location.getLineNumber(), location.getColumnNumber());
}
/**
* Create a location from an existing one, but with a different description
*
@@ -90,14 +90,14 @@ public class LocationImpl implements Location, Serializable {
public LocationImpl(String description, Location location) {
this(description, location.getURI(), location.getLineNumber(), location.getColumnNumber());
}
/**
* Obtain a <code>LocationImpl</code> from a {@link Location}. If <code>location</code> is
* already a <code>LocationImpl</code>, it is returned, otherwise it is copied.
* <p>
* This method is useful when an immutable and serializable location is needed, such as in locatable
* exceptions.
*
*
* @param location the location
* @return an immutable and serializable version of <code>location</code>
*/
@@ -110,19 +110,19 @@ public class LocationImpl implements Location, Serializable {
return new LocationImpl(location);
}
}
/**
* Get the description of this location
*
*
* @return the description (can be <code>null</code>)
*/
public String getDescription() {
return this.description;
}
/**
* Get the URI of this location
*
*
* @return the URI (<code>null</code> if unknown).
*/
public String getURI() {
@@ -131,22 +131,22 @@ public class LocationImpl implements Location, Serializable {
/**
* Get the line number of this location
*
*
* @return the line number (<code>-1</code> if unknown)
*/
public int getLineNumber() {
return this.line;
}
/**
* Get the column number of this location
*
*
* @return the column number (<code>-1</code> if unknown)
*/
public int getColumnNumber() {
return this.column;
}
/**
* @param padding The amount of lines before and after the error to include
* @return a source code snippet with the default padding
@@ -184,24 +184,24 @@ public class LocationImpl implements Location, Serializable {
&& testEquals(this.uri, other.getURI())
&& testEquals(this.description, other.getDescription());
}
return false;
}
@Override
public int hashCode() {
int hash = line ^ column;
if (uri != null) hash ^= uri.hashCode();
if (description != null) hash ^= description.hashCode();
return hash;
}
@Override
public String toString() {
return LocationUtils.toString(this);
}
/**
* Ensure serialized unknown location resolve to {@link Location#UNKNOWN}.
*
@@ -210,7 +210,7 @@ public class LocationImpl implements Location, Serializable {
private Object readResolve() {
return this.equals(Location.UNKNOWN) ? Location.UNKNOWN : this;
}
private boolean testEquals(Object object1, Object object2) {
if (object1 == object2) {
return true;
@@ -234,6 +234,8 @@ public final class StrutsConstants {
/** The name of the parameter to determine whether static field access will be allowed in OGNL expressions or not */
public static final String STRUTS_ALLOW_STATIC_FIELD_ACCESS = "struts.ognl.allowStaticFieldAccess";
public static final String STRUTS_MEMBER_ACCESS = "struts.securityMemberAccess";
public static final String STRUTS_OGNL_GUARD = "struts.ognlGuard";
/** The com.opensymphony.xwork2.validator.ActionValidatorManager implementation class */
@@ -49,6 +49,7 @@ import com.opensymphony.xwork2.inject.ContainerBuilder;
import com.opensymphony.xwork2.inject.Scope;
import com.opensymphony.xwork2.ognl.BeanInfoCacheFactory;
import com.opensymphony.xwork2.ognl.ExpressionCacheFactory;
import com.opensymphony.xwork2.ognl.SecurityMemberAccess;
import com.opensymphony.xwork2.security.AcceptedPatternsChecker;
import com.opensymphony.xwork2.security.ExcludedPatternsChecker;
import com.opensymphony.xwork2.security.NotExcludedAcceptedPatternsChecker;
@@ -435,6 +436,7 @@ public class StrutsBeanSelectionProvider extends AbstractBeanSelectionProvider {
alias(ExpressionCacheFactory.class, StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_FACTORY, builder, props, Scope.SINGLETON);
alias(BeanInfoCacheFactory.class, StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_FACTORY, builder, props, Scope.SINGLETON);
alias(SecurityMemberAccess.class, StrutsConstants.STRUTS_MEMBER_ACCESS, builder, props, Scope.PROTOTYPE);
alias(OgnlGuard.class, StrutsConstants.STRUTS_OGNL_GUARD, builder, props, Scope.SINGLETON);
alias(QueryStringBuilder.class, StrutsConstants.STRUTS_URL_QUERY_STRING_BUILDER, builder, props, Scope.SINGLETON);
@@ -218,9 +218,6 @@ struts.mapper.alwaysSelectFullNamespace=false
### Whether to allow static field access in OGNL expressions or not
struts.ognl.allowStaticFieldAccess=true
### Whether to allow static method access in OGNL expressions or not
struts.ognl.allowStaticMethodAccess=false
### Whether to throw a RuntimeException when a property is not found
### in an expression, or when the expression evaluation fails
struts.el.throwExceptionOnFailure=false
@@ -228,10 +225,6 @@ struts.el.throwExceptionOnFailure=false
### Logs as Warnings properties that are not found (very verbose)
struts.ognl.logMissingProperties=false
### Caches parsed OGNL expressions, but can lead to memory leaks
### if the application generates a lot of different expressions
struts.ognl.enableExpressionCache=true
### Specify the OGNL expression cache factory and BeanInfo cache factory to use.
### Currently, the default implementations are used, but can be replaced with custom ones if desired.
# struts.ognl.expressionCacheFactory=customOgnlExpressionCacheFactory
+2 -1
View File
@@ -166,8 +166,9 @@
class="com.opensymphony.xwork2.validator.DefaultValidatorFileParser"/>
<bean class="com.opensymphony.xwork2.ognl.OgnlUtil"/>
<bean name="struts" class="com.opensymphony.xwork2.ognl.SecurityMemberAccess" scope="prototype"/>
<bean type="org.apache.struts2.ognl.OgnlGuard" name="struts"
class="org.apache.struts2.ognl.StrutsOgnlGuard" />
class="org.apache.struts2.ognl.StrutsOgnlGuard"/>
<bean type="com.opensymphony.xwork2.util.TextParser" name="struts"
class="com.opensymphony.xwork2.util.OgnlTextParser" scope="singleton"/>
@@ -31,33 +31,6 @@ public class OgnlUtilStrutsTest extends StrutsInternalTestCase {
ognlUtil = container.getInstance(OgnlUtil.class);
}
public void testDefaultExcludes() {
ognlUtil.setExcludedClasses("");
ognlUtil.setExcludedPackageNames("");
ognlUtil.setExcludedPackageNamePatterns("");
assertFalse(ognlUtil.getExcludedClasses().isEmpty());
assertFalse(ognlUtil.getExcludedPackageNames().isEmpty());
try {
ognlUtil.getExcludedClasses().clear();
fail("Missing the expected Exception");
} catch (Exception ex) {
assertTrue(ex instanceof UnsupportedOperationException);
}
try {
ognlUtil.getExcludedPackageNames().clear();
fail("Missing the expected Exception");
} catch (Exception ex) {
assertTrue(ex instanceof UnsupportedOperationException);
}
try {
ognlUtil.getExcludedPackageNamePatterns().clear();
fail("Missing the expected Exception");
} catch (Exception ex) {
assertTrue(ex instanceof UnsupportedOperationException);
}
}
public void testAccessToSizeMethod() throws Exception {
// given
TestArrayBean bean = new TestArrayBean();
@@ -57,14 +57,11 @@ import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertThrows;
public class OgnlUtilTest extends XWorkTestCase {
@@ -889,11 +886,11 @@ public class OgnlUtilTest extends XWorkTestCase {
public void testBeanMapExpressions() throws OgnlException, NoSuchMethodException {
Foo foo = new Foo();
ognlUtil.setExcludedPackageNames(
"com.opensymphony.xwork2.ognl."
);
Map<String, Object> context = ognlUtil.createDefaultContext(foo);
SecurityMemberAccess sma = (SecurityMemberAccess) ((OgnlContext) context).getMemberAccess();
sma.useExcludedPackageNames("com.opensymphony.xwork2.ognl");
String expression = "%{\n" +
"(#request.a=#@org.apache.commons.collections.BeanMap@{}) +\n" +
@@ -910,8 +907,7 @@ public class OgnlUtilTest extends XWorkTestCase {
assertEquals(foo.getTitle(), expression);
SecurityMemberAccess sma = (SecurityMemberAccess) ((OgnlContext) context).getMemberAccess();
assertFalse(sma.isAccessible(context, sma, sma.getClass().getDeclaredMethod("useExcludedClasses", Set.class), "excludedClasses"));
assertFalse(sma.isAccessible(context, sma, sma.getClass().getDeclaredMethod("useExcludedClasses", String.class), "excludedClasses"));
}
public void testNullProperties() {
@@ -1181,22 +1177,16 @@ public class OgnlUtilTest extends XWorkTestCase {
assertNull(expected);
}
public void testAllowCallingMethodsOnObjectClassInDevModeFalse() {
Exception expected = null;
try {
ognlUtil.setExcludedClasses(Foo.class.getName());
ognlUtil.setDevModeExcludedClasses("");
ognlUtil.setDevMode(Boolean.FALSE.toString());
public void testExclusionListDevModeOnOff() throws Exception {
ognlUtil.setDevModeExcludedClasses(Foo.class.getName());
Foo foo = new Foo();
Foo foo = new Foo();
String result = (String) ognlUtil.getValue("toString", ognlUtil.createDefaultContext(foo), foo, String.class);
assertEquals("Foo", result);
} catch (OgnlException e) {
expected = e;
}
assertNotNull(expected);
assertSame(NoSuchPropertyException.class, expected.getClass());
assertEquals("com.opensymphony.xwork2.util.Foo.toString", expected.getMessage());
ognlUtil.setDevMode(Boolean.TRUE.toString());
OgnlException e = assertThrows(OgnlException.class, () -> ognlUtil.getValue("toString", ognlUtil.createDefaultContext(foo), foo, String.class));
assertThat(e).hasMessageContaining("com.opensymphony.xwork2.util.Foo.toString");
ognlUtil.setDevMode(Boolean.FALSE.toString());
assertEquals("Foo", (String) ognlUtil.getValue("toString", ognlUtil.createDefaultContext(foo), foo, String.class));
}
public void testAvoidCallingMethodsOnObjectClassUpperCased() {
@@ -1338,18 +1328,6 @@ public class OgnlUtilTest extends XWorkTestCase {
assertEquals(expected.getMessage(), "It isn't a simple method which can be called!");
}
public void testXworkTestCaseOgnlUtilExclusions() {
internalTestInitialEmptyOgnlUtilExclusions(ognlUtil);
internalTestOgnlUtilExclusionsImmutable(ognlUtil);
}
public void testDefaultOgnlUtilExclusions() {
OgnlUtil basicOgnlUtil = new OgnlUtil();
internalTestInitialEmptyOgnlUtilExclusions(basicOgnlUtil);
internalTestOgnlUtilExclusionsImmutable(basicOgnlUtil);
}
public void testDefaultOgnlUtilAlternateConstructorArguments() {
// Code coverage test for the OgnlUtil alternate constructor method, and verify expected behaviour.
try {
@@ -1366,85 +1344,6 @@ public class OgnlUtilTest extends XWorkTestCase {
}
}
public void testDefaultOgnlUtilExclusionsAlternateConstructorPopulated() {
OgnlUtil basicOgnlUtil = new OgnlUtil(new DefaultOgnlExpressionCacheFactory<>(),
new DefaultOgnlBeanInfoCacheFactory<>(),
new StrutsOgnlGuard());
internalTestInitialEmptyOgnlUtilExclusions(basicOgnlUtil);
internalTestOgnlUtilExclusionsImmutable(basicOgnlUtil);
}
public void testOgnlUtilExcludedAdditivity() {
Set<String> excludedClasses;
Set<Pattern> excludedPackageNamePatterns;
Iterator<Pattern> excludedPackageNamePatternsIterator;
Set<String> excludedPackageNames;
ognlUtil.setExcludedClasses("java.lang.String,java.lang.Integer");
internalTestOgnlUtilExclusionsImmutable(ognlUtil);
excludedClasses = ognlUtil.getExcludedClasses();
assertNotNull("initial excluded classes null?", excludedClasses);
assertEquals("initial excluded classes size not 2 after adds?", 2, excludedClasses.size());
assertTrue("String not in exclusions?", excludedClasses.contains(String.class.getName()));
assertTrue("Integer not in exclusions?", excludedClasses.contains(Integer.class.getName()));
ognlUtil.setExcludedClasses("java.lang.Boolean,java.lang.Double");
internalTestOgnlUtilExclusionsImmutable(ognlUtil);
excludedClasses = ognlUtil.getExcludedClasses();
assertNotNull("updated excluded classes null?", excludedClasses);
assertEquals("updated excluded classes size not 4 after adds?", 4, excludedClasses.size());
assertTrue("String not in exclusions?", excludedClasses.contains(String.class.getName()));
assertTrue("Integer not in exclusions?", excludedClasses.contains(Integer.class.getName()));
assertTrue("String not in exclusions?", excludedClasses.contains(Boolean.class.getName()));
assertTrue("Integer not in exclusions?", excludedClasses.contains(Double.class.getName()));
ognlUtil.setExcludedPackageNamePatterns("fakepackage1.*,fakepackage2.*");
internalTestOgnlUtilExclusionsImmutable(ognlUtil);
excludedPackageNamePatterns = ognlUtil.getExcludedPackageNamePatterns();
assertNotNull("initial excluded package name patterns null?", excludedPackageNamePatterns);
assertEquals("initial excluded package name patterns size not 2 after adds?", 2, excludedPackageNamePatterns.size());
excludedPackageNamePatternsIterator = excludedPackageNamePatterns.iterator();
Set<String> patternStrings = new HashSet<>();
while (excludedPackageNamePatternsIterator.hasNext()) {
Pattern pattern = excludedPackageNamePatternsIterator.next();
patternStrings.add(pattern.pattern());
}
assertTrue("fakepackage1.* not in exclusions?", patternStrings.contains("fakepackage1.*"));
assertTrue("fakepackage2.* not in exclusions?", patternStrings.contains("fakepackage2.*"));
ognlUtil.setExcludedPackageNamePatterns("fakepackage3.*,fakepackage4.*");
internalTestOgnlUtilExclusionsImmutable(ognlUtil);
excludedPackageNamePatterns = ognlUtil.getExcludedPackageNamePatterns();
assertNotNull("updated excluded package name patterns null?", excludedPackageNamePatterns);
assertEquals("updated excluded package name patterns size not 4 after adds?", 4, excludedPackageNamePatterns.size());
excludedPackageNamePatternsIterator = excludedPackageNamePatterns.iterator();
patternStrings = new HashSet<>();
while (excludedPackageNamePatternsIterator.hasNext()) {
Pattern pattern = excludedPackageNamePatternsIterator.next();
patternStrings.add(pattern.pattern());
}
assertTrue("fakepackage1.* not in exclusions?", patternStrings.contains("fakepackage1.*"));
assertTrue("fakepackage2.* not in exclusions?", patternStrings.contains("fakepackage2.*"));
assertTrue("fakepackage3.* not in exclusions?", patternStrings.contains("fakepackage3.*"));
assertTrue("fakepackage4.* not in exclusions?", patternStrings.contains("fakepackage4.*"));
ognlUtil.setExcludedPackageNames("fakepackage1.package,fakepackage2.package");
internalTestOgnlUtilExclusionsImmutable(ognlUtil);
excludedPackageNames = ognlUtil.getExcludedPackageNames();
assertNotNull("initial exluded package names null?", excludedPackageNames);
assertEquals("initial exluded package names not 2 after adds?", 2, excludedPackageNames.size());
assertTrue("fakepackage1.package not in exclusions?", excludedPackageNames.contains("fakepackage1.package"));
assertTrue("fakepackage2.package not in exclusions?", excludedPackageNames.contains("fakepackage2.package"));
ognlUtil.setExcludedPackageNames("fakepackage3.package,fakepackage4.package");
internalTestOgnlUtilExclusionsImmutable(ognlUtil);
excludedPackageNames = ognlUtil.getExcludedPackageNames();
assertNotNull("updated exluded package names null?", excludedPackageNames);
assertEquals("updated exluded package names not 4 after adds?", 4, excludedPackageNames.size());
assertTrue("fakepackage1.package not in exclusions?", excludedPackageNames.contains("fakepackage1.package"));
assertTrue("fakepackage2.package not in exclusions?", excludedPackageNames.contains("fakepackage2.package"));
assertTrue("fakepackage3.package not in exclusions?", excludedPackageNames.contains("fakepackage3.package"));
assertTrue("fakepackage4.package not in exclusions?", excludedPackageNames.contains("fakepackage4.package"));
}
/**
* Ensure getValue:
* 1) When allowStaticFieldAccess true - Permits public static field access,
@@ -1626,55 +1525,6 @@ public class OgnlUtilTest extends XWorkTestCase {
}
}
private void internalTestInitialEmptyOgnlUtilExclusions(OgnlUtil ognlUtilParam) {
Set<String> excludedClasses = ognlUtilParam.getExcludedClasses();
assertNotNull("parameter (default) exluded classes null?", excludedClasses);
assertTrue("parameter (default) exluded classes not empty?", excludedClasses.isEmpty());
Set<Pattern> excludedPackageNamePatterns = ognlUtilParam.getExcludedPackageNamePatterns();
assertNotNull("parameter (default) exluded package name patterns null?", excludedPackageNamePatterns);
assertTrue("parameter (default) exluded package name patterns not empty?", excludedPackageNamePatterns.isEmpty());
Set<String> excludedPackageNames = ognlUtilParam.getExcludedPackageNames();
assertNotNull("parameter (default) exluded package names null?", excludedPackageNames);
assertTrue("parameter (default) exluded package names not empty?", excludedPackageNames.isEmpty());
}
private void internalTestOgnlUtilExclusionsImmutable(OgnlUtil ognlUtilParam) {
Pattern somePattern = Pattern.compile("SomeRegexPattern");
assertImmutability(ognlUtilParam.getExcludedClasses(), Integer.class.getName());
assertImmutability(ognlUtilParam.getExcludedPackageNamePatterns(), somePattern);
assertImmutability(ognlUtilParam.getExcludedPackageNames(), "somepackagename");
assertImmutability(ognlUtilParam.getExcludedPackageExemptClasses(), Integer.class.getName());
}
public static <T> void assertImmutability(Collection<T> collection, T item) {
assertNotNull("Collection is null", collection);
try {
if (!collection.isEmpty()) {
collection.clear();
fail("Collection could be cleared");
}
} catch (UnsupportedOperationException uoe) {
// Expected failure
}
try {
collection.add(item);
fail("Collection could be added to");
} catch (UnsupportedOperationException uoe) {
// Expected failure
}
try {
int prevSize = collection.size();
collection.remove(item);
if (prevSize != collection.size()) {
fail("Collection could be removed from");
}
} catch (UnsupportedOperationException uoe) {
// Expected failure
}
}
public void testAccessContext() throws Exception {
Map<String, Object> context = ognlUtil.createDefaultContext(null);
@@ -1693,107 +1543,6 @@ public class OgnlUtilTest extends XWorkTestCase {
assertSame(that, root);
}
public void testSetExcludedPackageNames() {
assertThrows(ConfigurationException.class, () -> ognlUtil.setExcludedPackageNames("java.lang\njava.awt"));
assertThrows(ConfigurationException.class, () -> ognlUtil.setExcludedPackageNames("java.lang\tjava.awt"));
ConfigurationException e = assertThrows(ConfigurationException.class, () -> ognlUtil.setExcludedPackageNames("java.lang java.awt"));
assertTrue(e.getMessage().contains("erroneous whitespace characters"));
}
public void testGetExcludedPackageNames() {
// Getter should return an immutable collection
OgnlUtil util = new OgnlUtil();
util.setExcludedPackageNames("java.lang,java.awt");
assertEquals(util.getExcludedPackageNames().size(), 2);
try {
util.getExcludedPackageNames().clear();
} catch (Exception ex) {
assertTrue(ex instanceof UnsupportedOperationException);
} finally {
assertEquals(util.getExcludedPackageNames().size(), 2);
}
}
public void testGetExcludedPackageNamesAlternateConstructorPopulated() {
// Getter should return an immutable collection
OgnlUtil util = new OgnlUtil(new DefaultOgnlExpressionCacheFactory<>(),
new DefaultOgnlBeanInfoCacheFactory<>(),
new StrutsOgnlGuard());
util.setExcludedPackageNames("java.lang,java.awt");
assertEquals(util.getExcludedPackageNames().size(), 2);
try {
util.getExcludedPackageNames().clear();
} catch (Exception ex) {
assertTrue(ex instanceof UnsupportedOperationException);
} finally {
assertEquals(util.getExcludedPackageNames().size(), 2);
}
}
public void testGetExcludedClasses() {
// Getter should return an immutable collection
OgnlUtil util = new OgnlUtil();
util.setExcludedClasses("java.lang.Runtime,java.lang.ProcessBuilder,java.net.URL");
assertEquals(util.getExcludedClasses().size(), 3);
try {
util.getExcludedClasses().clear();
} catch (Exception ex) {
assertTrue(ex instanceof UnsupportedOperationException);
} finally {
assertEquals(util.getExcludedClasses().size(), 3);
}
}
public void testGetExcludedClassesAlternateConstructorPopulated() {
// Getter should return an immutable collection
OgnlUtil util = new OgnlUtil(new DefaultOgnlExpressionCacheFactory<>(),
new DefaultOgnlBeanInfoCacheFactory<>(),
new StrutsOgnlGuard());
util.setExcludedClasses("java.lang.Runtime,java.lang.ProcessBuilder,java.net.URL");
assertEquals(util.getExcludedClasses().size(), 3);
try {
util.getExcludedClasses().clear();
} catch (Exception ex) {
assertTrue(ex instanceof UnsupportedOperationException);
} finally {
assertEquals(util.getExcludedClasses().size(), 3);
}
}
public void testGetExcludedPackageNamePatterns() {
// Getter should return an immutable collection
OgnlUtil util = new OgnlUtil();
util.setExcludedPackageNamePatterns("java.lang.");
assertEquals(util.getExcludedPackageNamePatterns().size(), 1);
try {
util.getExcludedPackageNamePatterns().clear();
} catch (Exception ex) {
assertTrue(ex instanceof UnsupportedOperationException);
} finally {
assertEquals(util.getExcludedPackageNamePatterns().size(), 1);
}
}
public void testInvalidExcludedPackageNamePatterns() {
assertThrows(ConfigurationException.class, () -> ognlUtil.setExcludedPackageNamePatterns("["));
}
public void testGetExcludedPackageNamePatternsAlternateConstructorPopulated() {
// Getter should return an immutable collection
OgnlUtil util = new OgnlUtil(new DefaultOgnlExpressionCacheFactory<>(),
new DefaultOgnlBeanInfoCacheFactory<>(),
new StrutsOgnlGuard());
util.setExcludedPackageNamePatterns("java.lang.");
assertEquals(util.getExcludedPackageNamePatterns().size(), 1);
try {
util.getExcludedPackageNamePatterns().clear();
} catch (Exception ex) {
assertTrue(ex instanceof UnsupportedOperationException);
} finally {
assertEquals(util.getExcludedPackageNamePatterns().size(), 1);
}
}
public void testOgnlUtilDefaultCacheClass() throws OgnlException {
OgnlDefaultCache<Integer, String> defaultCache = new OgnlDefaultCache<>(2, 16, 0.75f);
assertEquals("Initial evictionLimit did not match initial value", 2, defaultCache.getEvictionLimit());
@@ -48,7 +48,6 @@ import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.struts2.StrutsConstants;
import org.apache.struts2.StrutsException;
import org.apache.struts2.config.DefaultPropertiesProvider;
import org.apache.struts2.config.StrutsXmlConfigurationProvider;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@@ -62,6 +61,8 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import static com.opensymphony.xwork2.ognl.SecurityMemberAccessTest.reflectField;
public class OgnlValueStackTest extends XWorkTestCase {
// Fields for static field access test
@@ -100,7 +101,6 @@ public class OgnlValueStackTest extends XWorkTestCase {
(CompoundRootAccessor) container.getInstance(PropertyAccessor.class, CompoundRoot.class.getName()),
container.getInstance(TextProvider.class, "system"), allowStaticFieldAccess);
container.inject(stack);
ognlUtil.setAllowStaticFieldAccess(Boolean.toString(allowStaticFieldAccess));
return stack;
}
@@ -120,7 +120,7 @@ public class OgnlValueStackTest extends XWorkTestCase {
* @param allowStaticField new allowStaticField configuration
* @return a new OgnlValueStackFactory with specified new configuration
*/
private OgnlValueStackFactory reloadValueStackFactory(Boolean allowStaticField) {
private OgnlValueStackFactory reloadValueStackFactory(boolean allowStaticField) {
try {
reloadTestContainerConfiguration(allowStaticField);
} catch (Exception ex) {
@@ -1150,14 +1150,13 @@ public class OgnlValueStackTest extends XWorkTestCase {
* Test a default OgnlValueStackFactory and OgnlValueStack generated by it
* when a default configuration is used.
*/
public void testOgnlValueStackFromOgnlValueStackFactoryDefaultConfig() {
public void testOgnlValueStackFromOgnlValueStackFactoryDefaultConfig() throws IllegalAccessException {
OgnlValueStackFactory ognlValueStackFactory = getValueStackFactory();
OgnlValueStack ognlValueStack = (OgnlValueStack) ognlValueStackFactory.createValueStack();
Object accessedValue;
// An OgnlValueStackFactory using a container config with default (from XWorkConfigurationProvider)
// static access flag values present should prevent staticMethodAccess but allow staticFieldAccess.
assertTrue("OgnlValueStackFactory staticFieldAccess (default flags) not true?", ognlValueStackFactory.containerAllowsStaticFieldAccess());
assertTrue("OgnlValueStackFactory staticFieldAccess (default flags) not true?",
reflectField(ognlValueStack.securityMemberAccess, "allowStaticFieldAccess"));
// An OgnlValueStack created from the above OgnlValueStackFactory should allow public field access,
// but prevent non-public field access. It should also deny static method access.
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@staticInteger100Method()");
@@ -1180,55 +1179,17 @@ public class OgnlValueStackTest extends XWorkTestCase {
assertNull("accessed private field (result not null) ?", accessedValue);
}
/**
* Test a raw OgnlValueStackFactory and OgnlValueStack generated by it
* when no static access flags are set (not present in configuration).
*/
public void testOgnlValueStackFromOgnlValueStackFactoryNoFlagsSet() {
OgnlValueStackFactory ognlValueStackFactory = reloadValueStackFactory(null);
OgnlValueStack ognlValueStack = (OgnlValueStack) ognlValueStackFactory.createValueStack();
Object accessedValue;
// An OgnlValueStackFactory using a container config with no static access flag values present
// (such as from a DefaultConfiguration vs. XWorkConfigurationProvider) should
// prevent staticMethodAccess AND prevent staticFieldAccess.
// Note: Under normal circumstances, explicit static access configuration flags should be present,
// but this specific check verifies what happens if those configuration flags are not present.
assertFalse("OgnlValueStackFactory staticFieldAccess (no flag present) not false?", ognlValueStackFactory.containerAllowsStaticFieldAccess());
// An OgnlValueStack created from the above OgnlValueStackFactory should prevent public field access,
// and prevent non-public field access. It should also deny static method access.
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@staticInteger100Method()");
assertNull("able to access static method (result not null) ?", accessedValue);
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PUBLIC_ATTRIBUTE");
assertNull("able to access static final public field (result not null) ?", accessedValue);
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PUBLIC_ATTRIBUTE");
assertNull("able to access static public field (result not null) ?", accessedValue);
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PACKAGE_ATTRIBUTE");
assertNull("accessed final package field (result not null) ?", accessedValue);
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PACKAGE_ATTRIBUTE");
assertNull("accessed package field (result not null) ?", accessedValue);
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PROTECTED_ATTRIBUTE");
assertNull("accessed final protected field (result not null) ?", accessedValue);
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PROTECTED_ATTRIBUTE");
assertNull("accessed protected field (result not null) ?", accessedValue);
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PRIVATE_ATTRIBUTE");
assertNull("accessed final private field (result not null) ?", accessedValue);
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PRIVATE_ATTRIBUTE");
assertNull("accessed private field (result not null) ?", accessedValue);
}
/**
* Test a raw OgnlValueStackFactory and OgnlValueStack generated by it
* when static access flag is set to false.
*/
public void testOgnlValueStackFromOgnlValueStackFactoryNoStaticAccess() {
public void testOgnlValueStackFromOgnlValueStackFactoryNoStaticAccess() throws IllegalAccessException {
OgnlValueStackFactory ognlValueStackFactory = reloadValueStackFactory(false);
OgnlValueStack ognlValueStack = (OgnlValueStack) ognlValueStackFactory.createValueStack();
Object accessedValue;
// An OgnlValueStackFactory using a container config with both static access flags set false should
// prevent staticMethodAccess AND prevent staticFieldAccess.
assertFalse("OgnlValueStackFactory staticFieldAccess (set false) not false?", ognlValueStackFactory.containerAllowsStaticFieldAccess());
assertFalse("OgnlValueStackFactory staticFieldAccess (set false) not false?",
reflectField(ognlValueStack.securityMemberAccess, "allowStaticFieldAccess"));
// An OgnlValueStack created from the above OgnlValueStackFactory should prevent public field access,
// and prevent non-public field access. It should also deny static method access.
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@staticInteger100Method()");
@@ -1255,14 +1216,13 @@ public class OgnlValueStackTest extends XWorkTestCase {
* Test a raw OgnlValueStackFactory and OgnlValueStack generated by it
* when static access flag is set to true.
*/
public void testOgnlValueStackFromOgnlValueStackFactoryAllStaticAccess() {
public void testOgnlValueStackFromOgnlValueStackFactoryAllStaticAccess() throws IllegalAccessException {
OgnlValueStackFactory ognlValueStackFactory = reloadValueStackFactory(true);
OgnlValueStack ognlValueStack = (OgnlValueStack) ognlValueStackFactory.createValueStack();
Object accessedValue;
// An OgnlValueStackFactory using a container config with both static access flags set true should
// allow both staticMethodAccess AND staticFieldAccess.
assertTrue("OgnlValueStackFactory staticFieldAccess (set true) not true?", ognlValueStackFactory.containerAllowsStaticFieldAccess());
assertTrue("OgnlValueStackFactory staticFieldAccess (set true) not true?",
reflectField(ognlValueStack.securityMemberAccess, "allowStaticFieldAccess"));
// An OgnlValueStack created from the above OgnlValueStackFactory should allow public field access,
// but prevent non-public field access. It should also allow static method access.
accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@staticInteger100Method()");
@@ -1285,17 +1245,12 @@ public class OgnlValueStackTest extends XWorkTestCase {
assertNull("accessed private field (result not null) ?", accessedValue);
}
private void reloadTestContainerConfiguration(Boolean allowStaticField) throws Exception {
private void reloadTestContainerConfiguration(boolean allowStaticField) throws Exception {
loadConfigurationProviders(new StubConfigurationProvider() {
@Override
public void register(ContainerBuilder builder,
LocatableProperties props) throws ConfigurationException {
// null values simulate undefined (by removing).
// undefined values then should be evaluated to false
props.remove(StrutsConstants.STRUTS_ALLOW_STATIC_FIELD_ACCESS);
if (allowStaticField != null) {
props.setProperty(StrutsConstants.STRUTS_ALLOW_STATIC_FIELD_ACCESS, "" + allowStaticField);
}
props.setProperty(StrutsConstants.STRUTS_ALLOW_STATIC_FIELD_ACCESS, String.valueOf(allowStaticField));
}
});
ognlUtil = container.getInstance(OgnlUtil.class);
@@ -19,8 +19,11 @@
package com.opensymphony.xwork2.ognl;
import com.opensymphony.xwork2.TestBean;
import com.opensymphony.xwork2.config.ConfigurationException;
import com.opensymphony.xwork2.test.TestBean2;
import com.opensymphony.xwork2.util.TextParseUtil;
import com.opensymphony.xwork2.util.Foo;
import ognl.MemberAccess;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.junit.Before;
import org.junit.Test;
@@ -28,16 +31,17 @@ import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.regex.Pattern;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -58,6 +62,87 @@ public class SecurityMemberAccessTest {
sma = new SecurityMemberAccess(allowStaticFieldAccess);
}
private <T> T reflectField(String fieldName) throws IllegalAccessException {
return reflectField(sma, fieldName);
}
public static <T> T reflectField(Object instance, String fieldName) throws IllegalAccessException {
return (T) FieldUtils.readField(instance, fieldName, true);
}
@Test
public void defaultExclusionList() throws Exception {
Set<String> excludedClasses = reflectField("excludedClasses");
assertThat(excludedClasses).containsExactly(Object.class.getName());
assignNewSma(false);
excludedClasses = reflectField("excludedClasses");
assertThat(excludedClasses).containsExactlyInAnyOrder(Object.class.getName(), Class.class.getName());
}
@Test
public void configurationCollectionsImmutable() throws Exception {
List<String> fields = Arrays.asList(
"excludedClasses",
"excludedPackageNames",
"excludedPackageNamePatterns",
"excludedPackageExemptClasses",
"allowlistClasses",
"allowlistPackageNames",
"excludeProperties",
"acceptProperties");
for (String field : fields) {
Collection<String> fieldVal = reflectField(field);
assertThrows(UnsupportedOperationException.class, () -> fieldVal.add("foo"));
if (!fieldVal.isEmpty()) {
String firstVal = fieldVal.iterator().next();
assertThrows(UnsupportedOperationException.class, () -> fieldVal.remove(firstVal));
assertThrows(UnsupportedOperationException.class, fieldVal::clear);
}
}
}
@Test
public void exclusionListsAreAdditive_classes() throws Exception {
Collection<String> fieldVal = reflectField("excludedClasses");
Set<String> existing = new HashSet<>(fieldVal);
Collection<String> newExcludedClasses = Arrays.asList(FooBar.class.getName(), String.class.getName());
sma.useExcludedClasses(String.join(",", newExcludedClasses));
existing.addAll(newExcludedClasses);
fieldVal = reflectField("excludedClasses");
assertThat(fieldVal).containsExactlyInAnyOrderElementsOf(existing);
}
@Test
public void exclusionListsAreAdditive_packages() throws Exception {
sma.useExcludedPackageNames(Foo.class.getPackage().getName());
Collection<String> fieldVal = reflectField("excludedPackageNames");
Set<String> existing = new HashSet<>(fieldVal);
Collection<String> newExcludedPackages = Arrays.asList(FooBar.class.getPackage().getName(), String.class.getPackage().getName());
sma.useExcludedPackageNames(String.join(",", newExcludedPackages));
existing.addAll(newExcludedPackages);
fieldVal = reflectField("excludedPackageNames");
assertThat(fieldVal).containsExactlyInAnyOrderElementsOf(existing);
}
@Test
public void useExcludedPackageNames() {
assertThrows(ConfigurationException.class, () -> sma.useExcludedPackageNames("java.lang\njava.awt"));
assertThrows(ConfigurationException.class, () -> sma.useExcludedPackageNames("java.lang\tjava.awt"));
ConfigurationException e = assertThrows(ConfigurationException.class, () -> sma.useExcludedPackageNames("java.lang java.awt"));
assertTrue(e.getMessage().contains("erroneous whitespace characters"));
}
@Test
public void useExcludedPackagePatterns() {
ConfigurationException e = assertThrows(ConfigurationException.class, () -> sma.useExcludedPackageNamePatterns("["));
assertTrue(e.getMessage().contains("invalid regex"));
}
@Test
public void testWithoutClassExclusion() throws Exception {
// given
@@ -77,9 +162,7 @@ public class SecurityMemberAccessTest {
String propertyName = "stringField";
Member member = FooBar.class.getDeclaredMethod(formGetterName(propertyName));
Set<String> excluded = new HashSet<>();
excluded.add(FooBar.class.getName());
sma.useExcludedClasses(excluded);
sma.useExcludedClasses(FooBar.class.getName());
// when
boolean accessible = sma.isAccessible(context, target, member, propertyName);
@@ -120,9 +203,7 @@ public class SecurityMemberAccessTest {
String propertyName = "barLogic";
Member member = BarInterface.class.getMethod(propertyName);
Set<String> excluded = new HashSet<>();
excluded.add(BarInterface.class.getName());
sma.useExcludedClasses(excluded);
sma.useExcludedClasses(BarInterface.class.getName());
// when
boolean accessible = sma.isAccessible(context, target, member, propertyName);
@@ -137,9 +218,7 @@ public class SecurityMemberAccessTest {
String propertyName = "fooLogic";
Member member = FooBar.class.getMethod(propertyName);
Set<String> excluded = new HashSet<>();
excluded.add(BarInterface.class.getName());
sma.useExcludedClasses(excluded);
sma.useExcludedClasses(BarInterface.class.getName());
// when
boolean accessible = sma.isAccessible(context, target, member, propertyName);
@@ -154,9 +233,7 @@ public class SecurityMemberAccessTest {
String propertyName = "barLogic";
Member member = BarInterface.class.getMethod(propertyName);
Set<String> excluded = new HashSet<>();
excluded.add(BarInterface.class.getName());
sma.useExcludedClasses(excluded);
sma.useExcludedClasses(BarInterface.class.getName());
// when
boolean accessible = sma.isAccessible(context, target, member, propertyName);
@@ -171,9 +248,7 @@ public class SecurityMemberAccessTest {
String propertyName = "barLogic";
Member member = BarInterface.class.getMethod(propertyName);
Set<String> excluded = new HashSet<>();
excluded.add(FooInterface.class.getName());
sma.useExcludedClasses(excluded);
sma.useExcludedClasses(FooInterface.class.getName());
// when
boolean accessible = sma.isAccessible(context, target, member, propertyName);
@@ -185,9 +260,7 @@ public class SecurityMemberAccessTest {
@Test
public void testPackageExclusion() throws Exception {
// given
Set<Pattern> excluded = new HashSet<>();
excluded.add(Pattern.compile("^" + FooBar.class.getPackage().getName().replaceAll("\\.", "\\\\.") + ".*"));
sma.useExcludedPackageNamePatterns(excluded);
sma.useExcludedPackageNamePatterns("^" + FooBar.class.getPackage().getName().replaceAll("\\.", "\\\\.") + ".*");
String propertyName = "stringField";
Member member = FooBar.class.getMethod(formGetterName(propertyName));
@@ -202,13 +275,9 @@ public class SecurityMemberAccessTest {
@Test
public void testPackageExclusionExemption() throws Exception {
// given
Set<Pattern> excluded = new HashSet<>();
excluded.add(Pattern.compile("^" + FooBar.class.getPackage().getName().replaceAll("\\.", "\\\\.") + ".*"));
sma.useExcludedPackageNamePatterns(excluded);
sma.useExcludedPackageNamePatterns("^" + FooBar.class.getPackage().getName().replaceAll("\\.", "\\\\.") + ".*");
Set<String> allowed = new HashSet<>();
allowed.add(FooBar.class.getName());
sma.useExcludedPackageExemptClasses(allowed);
sma.useExcludedPackageExemptClasses(FooBar.class.getName());
String propertyName = "stringField";
Member member = FooBar.class.getMethod(formGetterName(propertyName));
@@ -223,9 +292,7 @@ public class SecurityMemberAccessTest {
@Test
public void testPackageNameExclusion() throws Exception {
// given
Set<String> excluded = new HashSet<>();
excluded.add(FooBar.class.getPackage().getName());
sma.useExcludedPackageNames(excluded);
sma.useExcludedPackageNames(FooBar.class.getPackage().getName());
String propertyName = "stringField";
Member member = FooBar.class.getMethod(formGetterName(propertyName));
@@ -240,13 +307,9 @@ public class SecurityMemberAccessTest {
@Test
public void testPackageNameExclusionExemption() throws Exception {
// given
Set<String> excluded = new HashSet<>();
excluded.add(FooBar.class.getPackage().getName());
sma.useExcludedPackageNames(excluded);
sma.useExcludedPackageNames(FooBar.class.getPackage().getName());
Set<String> allowed = new HashSet<>();
allowed.add(FooBar.class.getName());
sma.useExcludedPackageExemptClasses(allowed);
sma.useExcludedPackageExemptClasses(FooBar.class.getName());
String propertyName = "stringField";
Member member = FooBar.class.getMethod(formGetterName(propertyName));
@@ -261,14 +324,10 @@ public class SecurityMemberAccessTest {
@Test
public void testPackageNameExclusionExemption2() throws Exception {
// given
Set<String> excluded = new HashSet<>();
excluded.add(FooBar.class.getPackage().getName());
sma.useExcludedPackageNames(excluded);
sma.useExcludedPackageNames(FooBar.class.getPackage().getName());
// Exemption must exist for both classes (target and member) if they both match a banned package
Set<String> allowed = new HashSet<>();
allowed.add(BarInterface.class.getName());
sma.useExcludedPackageExemptClasses(allowed);
sma.useExcludedPackageExemptClasses(BarInterface.class.getName());
String propertyName = "barLogic";
Member member = BarInterface.class.getMethod(propertyName);
@@ -283,15 +342,10 @@ public class SecurityMemberAccessTest {
@Test
public void testPackageNameExclusionExemption3() throws Exception {
// given
Set<String> excluded = new HashSet<>();
excluded.add(FooBar.class.getPackage().getName());
sma.useExcludedPackageNames(excluded);
sma.useExcludedPackageNames(FooBar.class.getPackage().getName());
// Exemption must exist for both classes (target and member) if they both match a banned package
Set<String> allowed = new HashSet<>();
allowed.add(BarInterface.class.getName());
allowed.add(FooBar.class.getName());
sma.useExcludedPackageExemptClasses(allowed);
sma.useExcludedPackageExemptClasses(String.join(",", BarInterface.class.getName(), FooBar.class.getName()));
String propertyName = "barLogic";
Member member = BarInterface.class.getMethod(propertyName);
@@ -306,9 +360,7 @@ public class SecurityMemberAccessTest {
@Test
public void testDefaultPackageExclusion() throws Exception {
// given
Set<Pattern> excluded = new HashSet<>();
excluded.add(Pattern.compile("^" + FooBar.class.getPackage().getName().replaceAll("\\.", "\\\\.") + ".*"));
sma.useExcludedPackageNamePatterns(excluded);
sma.useExcludedPackageNamePatterns("^" + FooBar.class.getPackage().getName().replaceAll("\\.", "\\\\.") + ".*");
Class<?> clazz = Class.forName("PackagelessAction");
@@ -321,7 +373,7 @@ public class SecurityMemberAccessTest {
@Test
public void testDefaultPackageExclusionSetting() throws Exception {
sma.disallowDefaultPackageAccess(true);
sma.useDisallowDefaultPackageAccess(Boolean.TRUE.toString());
Class<?> clazz = Class.forName("PackagelessAction");
boolean actual = sma.isAccessible(null, clazz.getConstructor().newInstance(), clazz.getMethod("execute"), null);
@@ -332,9 +384,7 @@ public class SecurityMemberAccessTest {
@Test
public void testDefaultPackageExclusion2() throws Exception {
// given
Set<Pattern> excluded = new HashSet<>();
excluded.add(Pattern.compile("^$"));
sma.useExcludedPackageNamePatterns(excluded);
sma.useExcludedPackageNamePatterns("^$");
Class<?> clazz = Class.forName("PackagelessAction");
@@ -368,7 +418,7 @@ public class SecurityMemberAccessTest {
@Test
public void testAccessStaticMethod() throws Exception {
// given
sma.useExcludedClasses(new HashSet<>(singletonList(Class.class.getName())));
sma.useExcludedClasses(Class.class.getName());
// when
Member method = StaticTester.class.getMethod("sayHello");
@@ -381,7 +431,7 @@ public class SecurityMemberAccessTest {
@Test
public void testAccessStaticField() throws Exception {
// given
sma.useExcludedClasses(new HashSet<>(singletonList(Class.class.getName())));
sma.useExcludedClasses(Class.class.getName());
// when
Member method = StaticTester.class.getField("MAX_VALUE");
@@ -395,7 +445,7 @@ public class SecurityMemberAccessTest {
public void testBlockedStaticFieldWhenFlagIsTrue() throws Exception {
// given
assignNewSma(true);
sma.useExcludedClasses(new HashSet<>(singletonList(Class.class.getName())));
sma.useExcludedClasses(Class.class.getName());
// when
Member method = StaticTester.class.getField("MAX_VALUE");
@@ -407,7 +457,7 @@ public class SecurityMemberAccessTest {
// public static final test
// given
assignNewSma(true);
sma.useExcludedClasses(new HashSet<>(singletonList(Class.class.getName())));
sma.useExcludedClasses(Class.class.getName());
// when
method = StaticTester.class.getField("MIN_VALUE");
@@ -419,7 +469,7 @@ public class SecurityMemberAccessTest {
// package static test
// given
assignNewSma(true);
sma.useExcludedClasses(new HashSet<>(singletonList(Class.class.getName())));
sma.useExcludedClasses(Class.class.getName());
// when
method = StaticTester.getFieldByName("PACKAGE_STRING");
@@ -431,7 +481,7 @@ public class SecurityMemberAccessTest {
// package final static test
// given
assignNewSma(true);
sma.useExcludedClasses(new HashSet<>(singletonList(Class.class.getName())));
sma.useExcludedClasses(Class.class.getName());
// when
method = StaticTester.getFieldByName("FINAL_PACKAGE_STRING");
@@ -443,7 +493,7 @@ public class SecurityMemberAccessTest {
// protected static test
// given
assignNewSma(true);
sma.useExcludedClasses(new HashSet<>(singletonList(Class.class.getName())));
sma.useExcludedClasses(Class.class.getName());
// when
method = StaticTester.getFieldByName("PROTECTED_STRING");
@@ -455,7 +505,7 @@ public class SecurityMemberAccessTest {
// protected final static test
// given
assignNewSma(true);
sma.useExcludedClasses(new HashSet<>(singletonList(Class.class.getName())));
sma.useExcludedClasses(Class.class.getName());
// when
method = StaticTester.getFieldByName("FINAL_PROTECTED_STRING");
@@ -467,7 +517,7 @@ public class SecurityMemberAccessTest {
// private static test
// given
assignNewSma(true);
sma.useExcludedClasses(new HashSet<>(singletonList(Class.class.getName())));
sma.useExcludedClasses(Class.class.getName());
// when
method = StaticTester.getFieldByName("PRIVATE_STRING");
@@ -479,7 +529,7 @@ public class SecurityMemberAccessTest {
// private final static test
// given
assignNewSma(true);
sma.useExcludedClasses(new HashSet<>(singletonList(Class.class.getName())));
sma.useExcludedClasses(Class.class.getName());
// when
method = StaticTester.getFieldByName("FINAL_PRIVATE_STRING");
@@ -582,7 +632,7 @@ public class SecurityMemberAccessTest {
@Test
public void testBlockedStaticFieldWhenClassIsExcluded() throws Exception {
// given
sma.useExcludedClasses(new HashSet<>(Arrays.asList(Class.class.getName(), StaticTester.class.getName())));
sma.useExcludedClasses(String.join(",", Class.class.getName(), StaticTester.class.getName()));
// when
Member method = StaticTester.class.getField("MAX_VALUE");
@@ -595,7 +645,7 @@ public class SecurityMemberAccessTest {
@Test
public void testBlockStaticMethodAccess() throws Exception {
// given
sma.useExcludedClasses(new HashSet<>(singletonList(Class.class.getName())));
sma.useExcludedClasses(Class.class.getName());
// when
Member method = StaticTester.class.getMethod("sayHello");
@@ -608,7 +658,7 @@ public class SecurityMemberAccessTest {
@Test
public void testBlockAccessIfClassIsExcluded() throws Exception {
// given
sma.useExcludedClasses(new HashSet<>(singletonList(Class.class.getName())));
sma.useExcludedClasses(Class.class.getName());
// when
Member method = Class.class.getMethod("getClassLoader");
@@ -621,7 +671,7 @@ public class SecurityMemberAccessTest {
@Test
public void testBlockAccessIfClassIsExcluded_2() throws Exception {
// given
sma.useExcludedClasses(new HashSet<>(singletonList(ClassLoader.class.getName())));
sma.useExcludedClasses(ClassLoader.class.getName());
// when
Member method = ClassLoader.class.getMethod("loadClass", String.class);
@@ -635,7 +685,7 @@ public class SecurityMemberAccessTest {
@Test
public void testAllowAccessIfClassIsNotExcluded() throws Exception {
// given
sma.useExcludedClasses(new HashSet<>(singletonList(ClassLoader.class.getName())));
sma.useExcludedClasses(ClassLoader.class.getName());
// when
Member method = Class.class.getMethod("getClassLoader");
@@ -648,7 +698,7 @@ public class SecurityMemberAccessTest {
@Test
public void testIllegalArgumentExceptionExpectedForTargetMemberMismatch() throws Exception {
// given
sma.useExcludedClasses(new HashSet<>(singletonList(Class.class.getName())));
sma.useExcludedClasses(Class.class.getName());
// when
Member method = ClassLoader.class.getMethod("loadClass", String.class);
@@ -667,7 +717,7 @@ public class SecurityMemberAccessTest {
@Test
public void testAccessPrimitiveInt() throws Exception {
// given
sma.useExcludedPackageNames(TextParseUtil.commaDelimitedStringToSet("java.lang.,ognl,javax"));
sma.useExcludedPackageNames("java.lang.,ognl,javax");
String propertyName = "intField";
Member member = FooBar.class.getMethod(formGetterName(propertyName));
@@ -682,7 +732,7 @@ public class SecurityMemberAccessTest {
@Test
public void testAccessPrimitiveDoubleWithNames() throws Exception {
// given
sma.useExcludedPackageNames(TextParseUtil.commaDelimitedStringToSet("ognl.,javax."));
sma.useExcludedPackageNames("ognl.,javax.");
Set<String> excluded = new HashSet<>();
@@ -691,7 +741,7 @@ public class SecurityMemberAccessTest {
excluded.add(System.class.getName());
excluded.add(Class.class.getName());
excluded.add(ClassLoader.class.getName());
sma.useExcludedClasses(excluded);
sma.useExcludedClasses(String.join(",", excluded));
String propertyName = "doubleValue";
double myDouble = 1;
@@ -735,9 +785,7 @@ public class SecurityMemberAccessTest {
@Test
public void testAccessPrimitiveDoubleWithPackageRegExs() throws Exception {
// given
Set<Pattern> patterns = new HashSet<>();
patterns.add(Pattern.compile("^java\\.lang\\..*"));
sma.useExcludedPackageNamePatterns(patterns);
sma.useExcludedPackageNamePatterns("^java\\.lang\\..*");
String propertyName = "doubleValue";
double myDouble = 1;
@@ -753,13 +801,11 @@ public class SecurityMemberAccessTest {
@Test
public void testAccessMemberAccessIsAccessible() throws Exception {
// given
Set<String> excluded = new HashSet<>();
excluded.add(ognl.MemberAccess.class.getName());
sma.useExcludedClasses(excluded);
sma.useExcludedClasses(MemberAccess.class.getName());
String propertyName = "excludedClasses";
String setter = "setExcludedClasses";
Member member = SecurityMemberAccess.class.getMethod(setter, Set.class);
String setter = "useExcludedClasses";
Member member = SecurityMemberAccess.class.getMethod(setter, String.class);
// when
boolean accessible = sma.isAccessible(context, sma, member, propertyName);
@@ -771,13 +817,11 @@ public class SecurityMemberAccessTest {
@Test
public void testAccessMemberAccessIsBlocked() throws Exception {
// given
Set<String> excluded = new HashSet<>();
excluded.add(SecurityMemberAccess.class.getName());
sma.useExcludedClasses(excluded);
sma.useExcludedClasses(SecurityMemberAccess.class.getName());
String propertyName = "excludedClasses";
String setter = "setExcludedClasses";
Member member = SecurityMemberAccess.class.getMethod(setter, Set.class);
String setter = "useExcludedClasses";
Member member = SecurityMemberAccess.class.getMethod(setter, String.class);
// when
boolean accessible = sma.isAccessible(context, sma, member, propertyName);
@@ -789,7 +833,7 @@ public class SecurityMemberAccessTest {
@Test
public void testPackageNameExclusionAsCommaDelimited() {
// given
sma.useExcludedPackageNames(TextParseUtil.commaDelimitedStringToSet("java.lang"));
sma.useExcludedPackageNames("java.lang");
// when
boolean actual = sma.isPackageExcluded(String.class);
@@ -801,36 +845,36 @@ public class SecurityMemberAccessTest {
@Test
public void classInclusion() throws Exception {
sma.useEnforceAllowlistEnabled(true);
sma.useEnforceAllowlistEnabled(Boolean.TRUE.toString());
TestBean2 bean = new TestBean2();
Method method = TestBean2.class.getMethod("getData");
assertFalse(sma.checkAllowlist(bean, method));
sma.useAllowlistClasses(new HashSet<>(singletonList(TestBean2.class.getName())));
sma.useAllowlistClasses(TestBean2.class.getName());
assertTrue(sma.checkAllowlist(bean, method));
}
@Test
public void packageInclusion() throws Exception {
sma.useEnforceAllowlistEnabled(true);
sma.useEnforceAllowlistEnabled(Boolean.TRUE.toString());
TestBean2 bean = new TestBean2();
Method method = TestBean2.class.getMethod("getData");
assertFalse(sma.checkAllowlist(bean, method));
sma.useAllowlistPackageNames(new HashSet<>(singletonList(TestBean2.class.getPackage().getName())));
sma.useAllowlistPackageNames(TestBean2.class.getPackage().getName());
assertTrue(sma.checkAllowlist(bean, method));
}
@Test
public void classInclusion_subclass() throws Exception {
sma.useEnforceAllowlistEnabled(true);
sma.useAllowlistClasses(new HashSet<>(singletonList(TestBean2.class.getName())));
sma.useEnforceAllowlistEnabled(Boolean.TRUE.toString());
sma.useAllowlistClasses(TestBean2.class.getName());
TestBean2 bean = new TestBean2();
Method method = TestBean2.class.getMethod("getName");
@@ -840,8 +884,8 @@ public class SecurityMemberAccessTest {
@Test
public void classInclusion_subclass_both() throws Exception {
sma.useEnforceAllowlistEnabled(true);
sma.useAllowlistClasses(new HashSet<>(asList(TestBean.class.getName(), TestBean2.class.getName())));
sma.useEnforceAllowlistEnabled(Boolean.TRUE.toString());
sma.useAllowlistClasses(String.join(",", TestBean.class.getName(), TestBean2.class.getName()));
TestBean2 bean = new TestBean2();
Method method = TestBean2.class.getMethod("getName");
@@ -851,8 +895,8 @@ public class SecurityMemberAccessTest {
@Test
public void packageInclusion_subclass() throws Exception {
sma.useEnforceAllowlistEnabled(true);
sma.useAllowlistPackageNames(new HashSet<>(singletonList(TestBean2.class.getPackage().getName())));
sma.useEnforceAllowlistEnabled(Boolean.TRUE.toString());
sma.useAllowlistPackageNames(TestBean2.class.getPackage().getName());
TestBean2 bean = new TestBean2();
Method method = TestBean2.class.getMethod("getName");
@@ -862,8 +906,10 @@ public class SecurityMemberAccessTest {
@Test
public void packageInclusion_subclass_both() throws Exception {
sma.useEnforceAllowlistEnabled(true);
sma.useAllowlistPackageNames(new HashSet<>(asList(TestBean.class.getPackage().getName(), TestBean2.class.getPackage().getName())));
sma.useEnforceAllowlistEnabled(Boolean.TRUE.toString());
sma.useAllowlistPackageNames(String.join(",",
TestBean.class.getPackage().getName(),
TestBean2.class.getPackage().getName()));
TestBean2 bean = new TestBean2();
Method method = TestBean2.class.getMethod("getName");
@@ -25,10 +25,7 @@ import org.apache.struts2.views.jsp.ActionTag;
import javax.servlet.jsp.tagext.TagSupport;
import java.lang.reflect.Member;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
public class SecurityMemberAccessInServletsTest extends StrutsInternalTestCase {
@@ -43,9 +40,7 @@ public class SecurityMemberAccessInServletsTest extends StrutsInternalTestCase {
// given
SecurityMemberAccess sma = new SecurityMemberAccess(true);
Set<Pattern> excluded = new HashSet<Pattern>();
excluded.add(Pattern.compile("^(?!javax\\.servlet\\..+)(javax\\..+)"));
sma.useExcludedPackageNamePatterns(excluded);
sma.useExcludedPackageNamePatterns("^(?!javax\\.servlet\\..+)(javax\\..+)");
String propertyName = "value";
Member member = TagSupport.class.getMethod("doStartTag");
@@ -61,9 +56,7 @@ public class SecurityMemberAccessInServletsTest extends StrutsInternalTestCase {
// given
SecurityMemberAccess sma = new SecurityMemberAccess(true);
Set<Pattern> excluded = new HashSet<>();
excluded.add(Pattern.compile("^javax\\..+"));
sma.useExcludedPackageNamePatterns(excluded);
sma.useExcludedPackageNamePatterns("^javax\\..+");
String propertyName = "value";
Member member = TagSupport.class.getMethod("doStartTag");
@@ -46,7 +46,7 @@ public class SecurityMemberAccessProxyTest extends XWorkTestCase {
"chaintoAOPedTestSubBeanAction", null, context);
SecurityMemberAccess sma = new SecurityMemberAccess(true);
sma.disallowProxyMemberAccess(true);
sma.useDisallowProxyMemberAccess(Boolean.TRUE.toString());
Member member = proxy.getAction().getClass().getMethod("isExposeProxy");
@@ -18,52 +18,5 @@
*/
package com.test;
import com.opensymphony.xwork2.ActionProxy;
import com.opensymphony.xwork2.XWorkTestCase;
import com.opensymphony.xwork2.config.providers.XmlConfigurationProvider;
import com.opensymphony.xwork2.ognl.SecurityMemberAccess;
import org.apache.struts2.config.StrutsXmlConfigurationProvider;
import java.lang.reflect.Member;
import java.util.HashMap;
import java.util.Map;
public class SecurityMemberAccessProxyTest extends XWorkTestCase {
private Map<String, Object> context;
@Override
public void setUp() throws Exception {
super.setUp();
context = new HashMap<>();
// Set up XWork
XmlConfigurationProvider provider = new StrutsXmlConfigurationProvider("com/opensymphony/xwork2/spring/actionContext-xwork.xml");
container.inject(provider);
loadConfigurationProviders(provider);
}
public void testProxyAccessIsBlocked() throws Exception {
ActionProxy proxy = actionProxyFactory.createActionProxy(null,
"chaintoAOPedTestSubBeanAction", null, context);
SecurityMemberAccess sma = new SecurityMemberAccess(true);
sma.disallowProxyMemberAccess(true);
Member member = proxy.getAction().getClass().getMethod("isExposeProxy");
boolean accessible = sma.isAccessible(context, proxy.getAction(), member, "");
assertFalse(accessible);
}
public void testProxyAccessIsAccessible() throws Exception {
ActionProxy proxy = actionProxyFactory.createActionProxy(null,
"chaintoAOPedTestSubBeanAction", null, context);
SecurityMemberAccess sma = new SecurityMemberAccess(true);
Member member = proxy.getAction().getClass().getMethod("isExposeProxy");
boolean accessible = sma.isAccessible(context, proxy.getAction(), member, "");
assertTrue(accessible);
}
public class SecurityMemberAccessProxyTest extends com.opensymphony.xwork2.ognl.SecurityMemberAccessProxyTest {
}