This is an automated email from the ASF dual-hosted git repository. kusal pushed a commit to branch WW-5339-node-constr in repository https://gitbox.apache.org/repos/asf/struts.git
commit c91f3c674b7c53a29ee802a63f8fd1d0f109e9a5 Author: Kusal Kithul-Godage <g...@kusal.io> AuthorDate: Mon Aug 28 22:24:12 2023 +1000 WW-5339 Add exclusion check to CompoundRootAccessor --- .../xwork2/ognl/accessor/CompoundRootAccessor.java | 46 ++++++++++++++++++++-- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/CompoundRootAccessor.java b/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/CompoundRootAccessor.java index 25bedba66..f3ad5c42a 100644 --- a/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/CompoundRootAccessor.java +++ b/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/CompoundRootAccessor.java @@ -19,6 +19,7 @@ package com.opensymphony.xwork2.ognl.accessor; import com.opensymphony.xwork2.inject.Inject; +import com.opensymphony.xwork2.ognl.OgnlUtil; import com.opensymphony.xwork2.ognl.OgnlValueStack; import com.opensymphony.xwork2.util.CompoundRoot; import com.opensymphony.xwork2.util.ValueStack; @@ -46,8 +47,12 @@ import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; +import java.util.regex.Pattern; +import static com.opensymphony.xwork2.ognl.SecurityMemberAccess.isExcludedPackageNamesStatic; +import static com.opensymphony.xwork2.ognl.SecurityMemberAccess.toPackageName; import static java.lang.String.format; +import static java.util.Collections.emptySet; import static org.apache.commons.lang3.BooleanUtils.toBoolean; /** @@ -77,12 +82,24 @@ public class CompoundRootAccessor implements PropertyAccessor, MethodAccessor, C private final static Class[] EMPTY_CLASS_ARRAY = new Class[0]; private static final Map<MethodCall, Boolean> invalidMethods = new ConcurrentHashMap<>(); private boolean devMode; + private Set<String> excludedClasses = emptySet(); + private Set<Pattern> excludedPackageNamePatterns = emptySet(); + private Set<String> excludedPackageNames = emptySet(); + private Set<String> excludedPackageExemptClasses = emptySet(); @Inject(StrutsConstants.STRUTS_DEVMODE) protected void setDevMode(String mode) { this.devMode = BooleanUtils.toBoolean(mode); } + @Inject + protected void setOgnlUtil(OgnlUtil ognlUtil) { + this.excludedClasses = ognlUtil.getExcludedClasses(); + this.excludedPackageNamePatterns = ognlUtil.getExcludedPackageNamePatterns(); + this.excludedPackageNames = ognlUtil.getExcludedPackageNames(); + this.excludedPackageExemptClasses = ognlUtil.getExcludedPackageExemptClasses(); + } + public void setProperty(Map context, Object target, Object name, Object value) throws OgnlException { CompoundRoot root = (CompoundRoot) target; OgnlContext ognlContext = (OgnlContext) context; @@ -272,9 +289,28 @@ public class CompoundRootAccessor implements PropertyAccessor, MethodAccessor, C return null; } + protected boolean isInaccessibleClass(Class<?> clazz) { + return isClassExcluded(clazz) || isPackageExcluded(clazz); + } + + protected boolean isPackageExcluded(Class<?> clazz) { + return !excludedPackageExemptClasses.contains(clazz.getName()) && (isExcludedPackageNames(clazz) || isExcludedPackageNamePatterns(clazz)); + } + + protected boolean isExcludedPackageNames(Class<?> clazz) { + return isExcludedPackageNamesStatic(clazz, excludedPackageNames); + } + + protected boolean isExcludedPackageNamePatterns(Class<?> clazz) { + return excludedPackageNamePatterns.stream().anyMatch(pattern -> pattern.matcher(toPackageName(clazz)).matches()); + } + + protected boolean isClassExcluded(Class<?> clazz) { + return excludedClasses.contains(clazz.getName()); + } + public Class classForName(String className, Map context) throws ClassNotFoundException { Object root = Ognl.getRoot(context); - try { if (root instanceof CompoundRoot) { if (className.startsWith("vs")) { @@ -292,8 +328,12 @@ public class CompoundRootAccessor implements PropertyAccessor, MethodAccessor, C } catch (Exception e) { LOG.debug("Got exception when tried to get class for name [{}]", className, e); } - - return Thread.currentThread().getContextClassLoader().loadClass(className); + Class loadedClass = Thread.currentThread().getContextClassLoader().loadClass(className); + if (isInaccessibleClass(loadedClass)) { + LOG.warn("Blocked attempt to construct new instance of class [{}] which is excluded", className); + return null; + } + return loadedClass; } private Class[] getArgTypes(Object[] args) {