Merge pull request #800 from apache/WW-5364-populate-allowlist

WW-5364 Automatically populate OGNL allowlist
This commit is contained in:
Kusal Kithul-Godage
2023-12-05 16:57:17 +11:00
committed by GitHub
20 changed files with 703 additions and 280 deletions
@@ -34,6 +34,19 @@
<constant name="struts.custom.i18n.resources" value="globalMessages" />
<constant name="struts.action.extension" value="action,," />
<constant name="struts.allowlist.enable" value="true" />
<constant name="struts.allowlist.packageNames"
value="
org.apache.struts2.showcase.model,
org.apache.struts2.showcase.conversion
"/>
<constant name="struts.allowlist.classes"
value="
org.apache.struts2.showcase.UITagExample$Language,
org.apache.struts2.showcase.UITagExample$VehicalType,
org.apache.struts2.showcase.UITagExample$VehicalSpecific
"/>
<constant name="struts.convention.package.locators.basePackage" value="org.apache.struts2.showcase" />
<constant name="struts.convention.result.path" value="/WEB-INF" />
@@ -0,0 +1,90 @@
/*
* 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;
import com.opensymphony.xwork2.config.Configuration;
import com.opensymphony.xwork2.config.ConfigurationException;
import com.opensymphony.xwork2.config.ConfigurationManager;
import com.opensymphony.xwork2.config.ConfigurationProvider;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.inject.ContainerBuilder;
import com.opensymphony.xwork2.inject.Context;
import com.opensymphony.xwork2.inject.Factory;
import com.opensymphony.xwork2.inject.Scope;
import com.opensymphony.xwork2.test.StubConfigurationProvider;
import com.opensymphony.xwork2.util.XWorkTestCaseHelper;
import com.opensymphony.xwork2.util.location.LocatableProperties;
import org.junit.After;
import org.junit.Before;
public abstract class XWorkJUnit4TestCase {
protected ConfigurationManager configurationManager;
protected Configuration configuration;
protected Container container;
protected ActionProxyFactory actionProxyFactory;
@Before
public void setUp() throws Exception {
configurationManager = XWorkTestCaseHelper.setUp();
configuration = configurationManager.getConfiguration();
container = configuration.getContainer();
actionProxyFactory = container.getInstance(ActionProxyFactory.class);
}
@After
public void tearDown() throws Exception {
XWorkTestCaseHelper.tearDown(configurationManager);
configurationManager = null;
configuration = null;
container = null;
actionProxyFactory = null;
}
protected void loadConfigurationProviders(ConfigurationProvider... providers) {
configurationManager = XWorkTestCaseHelper.loadConfigurationProviders(configurationManager, providers);
configuration = configurationManager.getConfiguration();
container = configuration.getContainer();
actionProxyFactory = container.getInstance(ActionProxyFactory.class);
}
protected void loadButAdd(final Class<?> type, final Object impl) {
loadButAdd(type, Container.DEFAULT_NAME, impl);
}
protected void loadButAdd(final Class<?> type, final String name, final Object impl) {
loadConfigurationProviders(new StubConfigurationProvider() {
@Override
public void register(ContainerBuilder builder,
LocatableProperties props) throws ConfigurationException {
builder.factory(type, name, new Factory() {
public Object create(Context context) throws Exception {
return impl;
}
@Override
public Class type() {
return impl.getClass();
}
}, Scope.SINGLETON);
}
});
}
}
@@ -68,10 +68,12 @@ import com.opensymphony.xwork2.factory.DefaultActionFactory;
import com.opensymphony.xwork2.factory.DefaultInterceptorFactory;
import com.opensymphony.xwork2.factory.DefaultResultFactory;
import com.opensymphony.xwork2.factory.DefaultUnknownHandlerFactory;
import com.opensymphony.xwork2.factory.DefaultValidatorFactory;
import com.opensymphony.xwork2.factory.InterceptorFactory;
import com.opensymphony.xwork2.factory.ResultFactory;
import com.opensymphony.xwork2.factory.StrutsConverterFactory;
import com.opensymphony.xwork2.factory.UnknownHandlerFactory;
import com.opensymphony.xwork2.factory.ValidatorFactory;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.inject.ContainerBuilder;
import com.opensymphony.xwork2.inject.Context;
@@ -107,6 +109,7 @@ import org.apache.struts2.conversion.StrutsConversionPropertiesProcessor;
import org.apache.struts2.conversion.StrutsTypeConverterCreator;
import org.apache.struts2.conversion.StrutsTypeConverterHolder;
import org.apache.struts2.ognl.OgnlGuard;
import org.apache.struts2.ognl.ProviderAllowlist;
import org.apache.struts2.ognl.StrutsOgnlGuard;
import java.util.ArrayList;
@@ -341,52 +344,13 @@ public class DefaultConfiguration implements Configuration {
fmFactoryRegistered = true;
}
}
builder.factory(ObjectFactory.class, Scope.SINGLETON);
builder.factory(ActionFactory.class, DefaultActionFactory.class, Scope.SINGLETON);
builder.factory(ResultFactory.class, DefaultResultFactory.class, Scope.SINGLETON);
builder.factory(InterceptorFactory.class, DefaultInterceptorFactory.class, Scope.SINGLETON);
builder.factory(com.opensymphony.xwork2.factory.ValidatorFactory.class, com.opensymphony.xwork2.factory.DefaultValidatorFactory.class, Scope.SINGLETON);
builder.factory(ConverterFactory.class, StrutsConverterFactory.class, Scope.SINGLETON);
builder.factory(UnknownHandlerFactory.class, DefaultUnknownHandlerFactory.class, Scope.SINGLETON);
builder.factory(FileManager.class, "system", DefaultFileManager.class, Scope.SINGLETON);
bootstrapFactories(builder);
bootstrapTypeConverters(builder);
if (!fmFactoryRegistered) {
builder.factory(FileManagerFactory.class, DefaultFileManagerFactory.class, Scope.SINGLETON);
}
builder.factory(ReflectionProvider.class, OgnlReflectionProvider.class, Scope.SINGLETON);
builder.factory(ValueStackFactory.class, OgnlValueStackFactory.class, Scope.SINGLETON);
builder.factory(XWorkConverter.class, Scope.SINGLETON);
builder.factory(ConversionPropertiesProcessor.class, StrutsConversionPropertiesProcessor.class, Scope.SINGLETON);
builder.factory(ConversionFileProcessor.class, DefaultConversionFileProcessor.class, Scope.SINGLETON);
builder.factory(ConversionAnnotationProcessor.class, DefaultConversionAnnotationProcessor.class, Scope.SINGLETON);
builder.factory(TypeConverterCreator.class, StrutsTypeConverterCreator.class, Scope.SINGLETON);
builder.factory(TypeConverterHolder.class, StrutsTypeConverterHolder.class, Scope.SINGLETON);
builder.factory(XWorkBasicConverter.class, Scope.SINGLETON);
builder.factory(TypeConverter.class, StrutsConstants.STRUTS_CONVERTER_COLLECTION, CollectionConverter.class, Scope.SINGLETON);
builder.factory(TypeConverter.class, StrutsConstants.STRUTS_CONVERTER_ARRAY, ArrayConverter.class, Scope.SINGLETON);
builder.factory(TypeConverter.class, StrutsConstants.STRUTS_CONVERTER_DATE, DateConverter.class, Scope.SINGLETON);
builder.factory(TypeConverter.class, StrutsConstants.STRUTS_CONVERTER_NUMBER, NumberConverter.class, Scope.SINGLETON);
builder.factory(TypeConverter.class, StrutsConstants.STRUTS_CONVERTER_STRING, StringConverter.class, Scope.SINGLETON);
builder.factory(TextProvider.class, "system", DefaultTextProvider.class, Scope.SINGLETON);
builder.factory(LocalizedTextProvider.class, StrutsLocalizedTextProvider.class, Scope.SINGLETON);
builder.factory(TextProviderFactory.class, StrutsTextProviderFactory.class, Scope.SINGLETON);
builder.factory(LocaleProviderFactory.class, DefaultLocaleProviderFactory.class, Scope.SINGLETON);
builder.factory(TextParser.class, OgnlTextParser.class, Scope.SINGLETON);
builder.factory(ObjectTypeDeterminer.class, DefaultObjectTypeDeterminer.class, Scope.SINGLETON);
builder.factory(PropertyAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON);
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);
for (Map.Entry<String, Object> entry : BOOTSTRAP_CONSTANTS.entrySet()) {
builder.constant(entry.getKey(), String.valueOf(entry.getValue()));
@@ -395,6 +359,57 @@ public class DefaultConfiguration implements Configuration {
return builder.create(true);
}
public static ContainerBuilder bootstrapFactories(ContainerBuilder builder) {
return builder
// TODO: SpringObjectFactoryTest fails when these are SINGLETON
.factory(ObjectFactory.class, Scope.PROTOTYPE)
.factory(ActionFactory.class, DefaultActionFactory.class, Scope.PROTOTYPE)
.factory(ResultFactory.class, DefaultResultFactory.class, Scope.PROTOTYPE)
.factory(InterceptorFactory.class, DefaultInterceptorFactory.class, Scope.PROTOTYPE)
.factory(ValidatorFactory.class, DefaultValidatorFactory.class, Scope.PROTOTYPE)
.factory(ConverterFactory.class, StrutsConverterFactory.class, Scope.PROTOTYPE)
.factory(UnknownHandlerFactory.class, DefaultUnknownHandlerFactory.class, Scope.PROTOTYPE)
.factory(FileManager.class, "system", DefaultFileManager.class, Scope.SINGLETON)
.factory(ReflectionProvider.class, OgnlReflectionProvider.class, Scope.SINGLETON)
.factory(ValueStackFactory.class, OgnlValueStackFactory.class, Scope.SINGLETON)
.factory(XWorkConverter.class, Scope.SINGLETON)
.factory(XWorkBasicConverter.class, Scope.SINGLETON)
.factory(ConversionPropertiesProcessor.class, StrutsConversionPropertiesProcessor.class, Scope.SINGLETON)
.factory(ConversionFileProcessor.class, DefaultConversionFileProcessor.class, Scope.SINGLETON)
.factory(ConversionAnnotationProcessor.class, DefaultConversionAnnotationProcessor.class, Scope.SINGLETON)
.factory(TypeConverterCreator.class, StrutsTypeConverterCreator.class, Scope.SINGLETON)
.factory(TypeConverterHolder.class, StrutsTypeConverterHolder.class, Scope.SINGLETON)
.factory(TextProvider.class, "system", DefaultTextProvider.class, Scope.SINGLETON)
.factory(LocalizedTextProvider.class, StrutsLocalizedTextProvider.class, Scope.SINGLETON)
.factory(TextProviderFactory.class, StrutsTextProviderFactory.class, Scope.SINGLETON)
.factory(LocaleProviderFactory.class, DefaultLocaleProviderFactory.class, Scope.SINGLETON)
.factory(TextParser.class, OgnlTextParser.class, Scope.SINGLETON)
.factory(ObjectTypeDeterminer.class, DefaultObjectTypeDeterminer.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON)
.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(ProviderAllowlist.class, Scope.SINGLETON)
.factory(ValueSubstitutor.class, EnvsValueSubstitutor.class, Scope.SINGLETON);
}
public static ContainerBuilder bootstrapTypeConverters(ContainerBuilder builder) {
return builder
.factory(TypeConverter.class, StrutsConstants.STRUTS_CONVERTER_COLLECTION, CollectionConverter.class, Scope.SINGLETON)
.factory(TypeConverter.class, StrutsConstants.STRUTS_CONVERTER_ARRAY, ArrayConverter.class, Scope.SINGLETON)
.factory(TypeConverter.class, StrutsConstants.STRUTS_CONVERTER_DATE, DateConverter.class, Scope.SINGLETON)
.factory(TypeConverter.class, StrutsConstants.STRUTS_CONVERTER_NUMBER, NumberConverter.class, Scope.SINGLETON)
.factory(TypeConverter.class, StrutsConstants.STRUTS_CONVERTER_STRING, StringConverter.class, Scope.SINGLETON);
}
/**
* <p>
* This builds the internal runtime configuration used by Xwork for finding and configuring Actions from the
@@ -20,62 +20,24 @@ package com.opensymphony.xwork2.config.providers;
import com.opensymphony.xwork2.ActionProxyFactory;
import com.opensymphony.xwork2.DefaultActionProxyFactory;
import com.opensymphony.xwork2.DefaultLocaleProviderFactory;
import com.opensymphony.xwork2.DefaultTextProvider;
import com.opensymphony.xwork2.DefaultUnknownHandlerManager;
import com.opensymphony.xwork2.FileManager;
import com.opensymphony.xwork2.FileManagerFactory;
import com.opensymphony.xwork2.LocaleProviderFactory;
import com.opensymphony.xwork2.LocalizedTextProvider;
import com.opensymphony.xwork2.ObjectFactory;
import com.opensymphony.xwork2.StrutsTextProviderFactory;
import com.opensymphony.xwork2.TextProvider;
import com.opensymphony.xwork2.TextProviderFactory;
import com.opensymphony.xwork2.UnknownHandlerManager;
import com.opensymphony.xwork2.config.Configuration;
import com.opensymphony.xwork2.config.ConfigurationException;
import com.opensymphony.xwork2.config.ConfigurationProvider;
import com.opensymphony.xwork2.config.impl.DefaultConfiguration;
import com.opensymphony.xwork2.conversion.ConversionAnnotationProcessor;
import com.opensymphony.xwork2.conversion.ConversionFileProcessor;
import com.opensymphony.xwork2.conversion.ConversionPropertiesProcessor;
import com.opensymphony.xwork2.conversion.NullHandler;
import com.opensymphony.xwork2.conversion.ObjectTypeDeterminer;
import com.opensymphony.xwork2.conversion.TypeConverterCreator;
import com.opensymphony.xwork2.conversion.TypeConverterHolder;
import com.opensymphony.xwork2.conversion.impl.ArrayConverter;
import com.opensymphony.xwork2.conversion.impl.CollectionConverter;
import com.opensymphony.xwork2.conversion.impl.DateConverter;
import com.opensymphony.xwork2.conversion.impl.DefaultConversionAnnotationProcessor;
import com.opensymphony.xwork2.conversion.impl.DefaultConversionFileProcessor;
import com.opensymphony.xwork2.conversion.impl.DefaultObjectTypeDeterminer;
import com.opensymphony.xwork2.conversion.impl.InstantiatingNullHandler;
import com.opensymphony.xwork2.conversion.impl.NumberConverter;
import com.opensymphony.xwork2.conversion.impl.StringConverter;
import com.opensymphony.xwork2.conversion.impl.XWorkBasicConverter;
import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
import com.opensymphony.xwork2.factory.ActionFactory;
import com.opensymphony.xwork2.factory.ConverterFactory;
import com.opensymphony.xwork2.factory.DefaultActionFactory;
import com.opensymphony.xwork2.factory.DefaultInterceptorFactory;
import com.opensymphony.xwork2.factory.DefaultResultFactory;
import com.opensymphony.xwork2.factory.DefaultUnknownHandlerFactory;
import com.opensymphony.xwork2.factory.InterceptorFactory;
import com.opensymphony.xwork2.factory.ResultFactory;
import com.opensymphony.xwork2.factory.StrutsConverterFactory;
import com.opensymphony.xwork2.factory.UnknownHandlerFactory;
import com.opensymphony.xwork2.inject.ContainerBuilder;
import com.opensymphony.xwork2.inject.Scope;
import com.opensymphony.xwork2.ognl.BeanInfoCacheFactory;
import com.opensymphony.xwork2.ognl.DefaultOgnlBeanInfoCacheFactory;
import com.opensymphony.xwork2.ognl.DefaultOgnlExpressionCacheFactory;
import com.opensymphony.xwork2.ognl.ExpressionCacheFactory;
import com.opensymphony.xwork2.ognl.ObjectProxy;
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;
@@ -94,17 +56,11 @@ import com.opensymphony.xwork2.security.DefaultNotExcludedAcceptedPatternsChecke
import com.opensymphony.xwork2.security.ExcludedPatternsChecker;
import com.opensymphony.xwork2.security.NotExcludedAcceptedPatternsChecker;
import com.opensymphony.xwork2.util.CompoundRoot;
import com.opensymphony.xwork2.util.OgnlTextParser;
import com.opensymphony.xwork2.util.PatternMatcher;
import com.opensymphony.xwork2.util.StrutsLocalizedTextProvider;
import com.opensymphony.xwork2.util.TextParser;
import com.opensymphony.xwork2.util.ValueStackFactory;
import com.opensymphony.xwork2.util.WildcardHelper;
import com.opensymphony.xwork2.util.fs.DefaultFileManager;
import com.opensymphony.xwork2.util.fs.DefaultFileManagerFactory;
import com.opensymphony.xwork2.util.location.LocatableProperties;
import com.opensymphony.xwork2.util.reflection.ReflectionContextFactory;
import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
import com.opensymphony.xwork2.validator.ActionValidatorManager;
import com.opensymphony.xwork2.validator.AnnotationActionValidatorManager;
import com.opensymphony.xwork2.validator.DefaultActionValidatorManager;
@@ -114,15 +70,10 @@ import com.opensymphony.xwork2.validator.ValidatorFactory;
import com.opensymphony.xwork2.validator.ValidatorFileParser;
import ognl.MethodAccessor;
import ognl.PropertyAccessor;
import org.apache.struts2.conversion.StrutsConversionPropertiesProcessor;
import org.apache.struts2.conversion.StrutsTypeConverterCreator;
import org.apache.struts2.conversion.StrutsTypeConverterHolder;
import org.apache.struts2.dispatcher.HttpParameters;
import org.apache.struts2.dispatcher.Parameter;
import org.apache.struts2.interceptor.exec.ExecutorProvider;
import org.apache.struts2.interceptor.exec.StrutsExecutorProvider;
import org.apache.struts2.ognl.OgnlGuard;
import org.apache.struts2.ognl.StrutsOgnlGuard;
import org.apache.struts2.url.QueryStringBuilder;
import org.apache.struts2.url.QueryStringParser;
import org.apache.struts2.url.StrutsQueryStringBuilder;
@@ -162,96 +113,60 @@ public class StrutsDefaultConfigurationProvider implements ConfigurationProvider
}
@Override
public void register(ContainerBuilder builder, LocatableProperties props)
throws ConfigurationException {
public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException {
builder
.factory(ObjectFactory.class)
.factory(ActionFactory.class, DefaultActionFactory.class)
.factory(ResultFactory.class, DefaultResultFactory.class)
.factory(InterceptorFactory.class, DefaultInterceptorFactory.class)
.factory(com.opensymphony.xwork2.factory.ValidatorFactory.class, com.opensymphony.xwork2.factory.DefaultValidatorFactory.class)
.factory(ConverterFactory.class, StrutsConverterFactory.class)
.factory(UnknownHandlerFactory.class, DefaultUnknownHandlerFactory.class)
DefaultConfiguration.bootstrapFactories(builder)
.factory(FileManagerFactory.class, DefaultFileManagerFactory.class, Scope.SINGLETON)
.factory(ActionProxyFactory.class, DefaultActionProxyFactory.class, Scope.SINGLETON)
.factory(ObjectTypeDeterminer.class, DefaultObjectTypeDeterminer.class, Scope.SINGLETON)
.factory(ActionProxyFactory.class, DefaultActionProxyFactory.class, Scope.SINGLETON)
.factory(XWorkConverter.class, Scope.SINGLETON)
.factory(XWorkBasicConverter.class, Scope.SINGLETON)
.factory(ConversionPropertiesProcessor.class, StrutsConversionPropertiesProcessor.class, Scope.SINGLETON)
.factory(ConversionFileProcessor.class, DefaultConversionFileProcessor.class, Scope.SINGLETON)
.factory(ConversionAnnotationProcessor.class, DefaultConversionAnnotationProcessor.class, Scope.SINGLETON)
.factory(TypeConverterCreator.class, StrutsTypeConverterCreator.class, Scope.SINGLETON)
.factory(TypeConverterHolder.class, StrutsTypeConverterHolder.class, Scope.SINGLETON)
.factory(ValidatorFactory.class, DefaultValidatorFactory.class, Scope.SINGLETON)
.factory(ValidatorFileParser.class, DefaultValidatorFileParser.class, Scope.SINGLETON)
.factory(PatternMatcher.class, WildcardHelper.class, Scope.SINGLETON)
.factory(FileManager.class, "system", DefaultFileManager.class, Scope.SINGLETON)
.factory(FileManagerFactory.class, DefaultFileManagerFactory.class, Scope.SINGLETON)
.factory(ValueStackFactory.class, OgnlValueStackFactory.class, Scope.SINGLETON)
.factory(ValidatorFactory.class, DefaultValidatorFactory.class, Scope.SINGLETON)
.factory(ValidatorFileParser.class, DefaultValidatorFileParser.class, Scope.SINGLETON)
.factory(PatternMatcher.class, WildcardHelper.class, Scope.SINGLETON)
.factory(ReflectionProvider.class, OgnlReflectionProvider.class, Scope.SINGLETON)
.factory(ReflectionContextFactory.class, OgnlReflectionContextFactory.class, Scope.SINGLETON)
.factory(ReflectionContextFactory.class, OgnlReflectionContextFactory.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, Object.class.getName(), ObjectAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, Iterator.class.getName(), XWorkIteratorPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, Enumeration.class.getName(), XWorkEnumerationAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, Object.class.getName(), ObjectAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, Iterator.class.getName(), XWorkIteratorPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, Enumeration.class.getName(), XWorkEnumerationAccessor.class, Scope.SINGLETON)
.factory(UnknownHandlerManager.class, DefaultUnknownHandlerManager.class, Scope.SINGLETON)
.factory(UnknownHandlerManager.class, DefaultUnknownHandlerManager.class, Scope.SINGLETON)
// silly workarounds for ognl since there is no way to flush its caches
.factory(PropertyAccessor.class, List.class.getName(), XWorkListPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, ArrayList.class.getName(), XWorkListPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, HashSet.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, Set.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, HashMap.class.getName(), XWorkMapPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, Map.class.getName(), XWorkMapPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, Collection.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, ObjectProxy.class.getName(), ObjectProxyPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, HttpParameters.class.getName(), HttpParametersPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, Parameter.class.getName(), ParameterPropertyAccessor.class, Scope.SINGLETON)
// silly workarounds for ognl since there is no way to flush its caches
.factory(PropertyAccessor.class, List.class.getName(), XWorkListPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, ArrayList.class.getName(), XWorkListPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, HashSet.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, Set.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, HashMap.class.getName(), XWorkMapPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, Map.class.getName(), XWorkMapPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, Collection.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, ObjectProxy.class.getName(), ObjectProxyPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, HttpParameters.class.getName(), HttpParametersPropertyAccessor.class, Scope.SINGLETON)
.factory(PropertyAccessor.class, Parameter.class.getName(), ParameterPropertyAccessor.class, Scope.SINGLETON)
.factory(MethodAccessor.class, Object.class.getName(), XWorkMethodAccessor.class, Scope.SINGLETON)
.factory(MethodAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON)
.factory(MethodAccessor.class, Object.class.getName(), XWorkMethodAccessor.class, Scope.SINGLETON)
.factory(MethodAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON)
.factory(TextParser.class, OgnlTextParser.class, Scope.SINGLETON)
.factory(NullHandler.class, Object.class.getName(), InstantiatingNullHandler.class, Scope.SINGLETON)
.factory(ActionValidatorManager.class, AnnotationActionValidatorManager.class, Scope.SINGLETON)
.factory(ActionValidatorManager.class, "no-annotations", DefaultActionValidatorManager.class, Scope.SINGLETON)
.factory(NullHandler.class, Object.class.getName(), InstantiatingNullHandler.class, Scope.SINGLETON)
.factory(ActionValidatorManager.class, AnnotationActionValidatorManager.class, Scope.SINGLETON)
.factory(ActionValidatorManager.class, "no-annotations", DefaultActionValidatorManager.class, Scope.SINGLETON)
.factory(CollectionConverter.class, Scope.SINGLETON)
.factory(ArrayConverter.class, Scope.SINGLETON)
.factory(DateConverter.class, Scope.SINGLETON)
.factory(NumberConverter.class, Scope.SINGLETON)
.factory(StringConverter.class, Scope.SINGLETON)
.factory(TextProvider.class, "system", DefaultTextProvider.class, Scope.SINGLETON)
.factory(LocalizedTextProvider.class, StrutsLocalizedTextProvider.class, Scope.SINGLETON)
.factory(TextProviderFactory.class, StrutsTextProviderFactory.class, Scope.SINGLETON)
.factory(LocaleProviderFactory.class, DefaultLocaleProviderFactory.class, Scope.SINGLETON)
.factory(ExcludedPatternsChecker.class, DefaultExcludedPatternsChecker.class, Scope.PROTOTYPE)
.factory(AcceptedPatternsChecker.class, DefaultAcceptedPatternsChecker.class, Scope.PROTOTYPE)
.factory(NotExcludedAcceptedPatternsChecker.class, DefaultNotExcludedAcceptedPatternsChecker.class, Scope.SINGLETON)
.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)
.factory(DateConverter.class, Scope.SINGLETON)
.factory(NumberConverter.class, Scope.SINGLETON)
.factory(StringConverter.class, Scope.SINGLETON)
.factory(QueryStringBuilder.class, StrutsQueryStringBuilder.class, Scope.SINGLETON)
.factory(QueryStringParser.class, StrutsQueryStringParser.class, Scope.SINGLETON)
.factory(UrlEncoder.class, StrutsUrlEncoder.class, Scope.SINGLETON)
.factory(UrlDecoder.class, StrutsUrlDecoder.class, Scope.SINGLETON)
.factory(ExcludedPatternsChecker.class, DefaultExcludedPatternsChecker.class, Scope.PROTOTYPE)
.factory(AcceptedPatternsChecker.class, DefaultAcceptedPatternsChecker.class, Scope.PROTOTYPE)
.factory(NotExcludedAcceptedPatternsChecker.class, DefaultNotExcludedAcceptedPatternsChecker.class
, Scope.SINGLETON)
.factory(ValueSubstitutor.class, EnvsValueSubstitutor.class, Scope.SINGLETON)
.factory(QueryStringBuilder.class, StrutsQueryStringBuilder.class, Scope.SINGLETON)
.factory(QueryStringParser.class, StrutsQueryStringParser.class, Scope.SINGLETON)
.factory(UrlEncoder.class, StrutsUrlEncoder.class, Scope.SINGLETON)
.factory(UrlDecoder.class, StrutsUrlDecoder.class, Scope.SINGLETON)
.factory(ExecutorProvider.class, StrutsExecutorProvider.class, Scope.SINGLETON)
;
.factory(ExecutorProvider.class, StrutsExecutorProvider.class, Scope.SINGLETON);
for (Map.Entry<String, Object> entry : DefaultConfiguration.BOOTSTRAP_CONSTANTS.entrySet()) {
props.setProperty(entry.getKey(), String.valueOf(entry.getValue()));
@@ -45,9 +45,11 @@ import com.opensymphony.xwork2.util.location.LocatableProperties;
import com.opensymphony.xwork2.util.location.Location;
import com.opensymphony.xwork2.util.location.LocationUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.ognl.ProviderAllowlist;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
@@ -92,8 +94,10 @@ public abstract class XmlDocConfigurationProvider implements ConfigurationProvid
protected ObjectFactory objectFactory;
protected Map<String, String> dtdMappings = new HashMap<>();
protected Configuration configuration;
protected ProviderAllowlist providerAllowlist;
protected boolean throwExceptionOnDuplicateBeans = true;
protected ValueSubstitutor valueSubstitutor;
protected Set<Class<?>> allowlistClasses = new HashSet<>();
@Inject
public void setObjectFactory(ObjectFactory objectFactory) {
@@ -131,8 +135,22 @@ public abstract class XmlDocConfigurationProvider implements ConfigurationProvid
this.configuration = configuration;
}
private void registerAllowlist() {
providerAllowlist = configuration.getContainer().getInstance(ProviderAllowlist.class);
providerAllowlist.registerAllowlist(this, allowlistClasses);
}
@Override
public void destroy() {
providerAllowlist.clearAllowlist(this);
}
protected Class<?> allowAndLoadClass(String className) throws ClassNotFoundException {
Class<?> clazz = loadClass(className);
allowlistClasses.add(clazz);
allowlistClasses.addAll(ClassUtils.getAllSuperclasses(clazz));
allowlistClasses.addAll(ClassUtils.getAllInterfaces(clazz));
return clazz;
}
protected Class<?> loadClass(String className) throws ClassNotFoundException {
@@ -169,6 +187,7 @@ public abstract class XmlDocConfigurationProvider implements ConfigurationProvid
@Override
public void register(ContainerBuilder containerBuilder, LocatableProperties props) throws ConfigurationException {
allowlistClasses.clear();
Map<String, Node> loadedBeans = new HashMap<>();
for (Document doc : documents) {
iterateElementChildren(doc, child -> {
@@ -197,7 +216,7 @@ public abstract class XmlDocConfigurationProvider implements ConfigurationProvid
String name = child.getAttribute("name");
String impl = child.getAttribute("class");
try {
Class<?> classImpl = loadClass(impl);
Class<?> classImpl = ClassLoaderUtil.loadClass(impl, getClass());
if (BeanSelectionProvider.class.isAssignableFrom(classImpl)) {
BeanSelectionProvider provider = (BeanSelectionProvider) classImpl.newInstance();
provider.register(containerBuilder, props);
@@ -303,7 +322,7 @@ public abstract class XmlDocConfigurationProvider implements ConfigurationProvid
loadExtraConfiguration(doc);
}
if (reloads.size() > 0) {
if (!reloads.isEmpty()) {
reloadRequiredPackages(reloads);
}
@@ -312,6 +331,7 @@ public abstract class XmlDocConfigurationProvider implements ConfigurationProvid
}
declaredPackages.clear();
registerAllowlist();
configuration = null;
}
@@ -442,13 +462,8 @@ public abstract class XmlDocConfigurationProvider implements ConfigurationProvid
Location location = DomHelper.getLocationObject(actionElement);
if (location == null) {
LOG.warn("Location null for {}", className);
}
if (!className.isEmpty() && !verifyAction(className, name, location)) {
LOG.error("Unable to verify action [{}] with class [{}], from [{}]", name, className, location);
return;
if (!className.isEmpty()) {
verifyAction(className, name, location);
}
Map<String, ResultConfig> results;
@@ -499,27 +514,30 @@ public abstract class XmlDocConfigurationProvider implements ConfigurationProvid
*/
@Deprecated
protected boolean verifyAction(String className, String name, Location loc) {
return verifyAction(className, loc);
verifyAction(className, loc);
return true;
}
protected boolean verifyAction(String className, Location loc) {
protected void verifyAction(String className, Location loc) {
if (className.contains("{")) {
LOG.debug("Action class [{}] contains a wildcard replacement value, so it can't be verified", className);
return true;
return;
}
try {
Class<?> clazz = allowAndLoadClass(className);
if (objectFactory.isNoArgConstructorRequired()) {
Class<?> clazz = loadClass(className);
if (!Modifier.isPublic(clazz.getModifiers())) {
throw new ConfigurationException("Action class [" + className + "] is not public", loc);
}
clazz.getConstructor();
}
} catch (ClassNotFoundException e) {
LOG.debug("Class not found for action [{}]", className, e);
throw new ConfigurationException("Action class [" + className + "] not found", loc);
if (objectFactory.isNoArgConstructorRequired()) {
throw new ConfigurationException("Action class [" + className + "] not found", e, loc);
}
LOG.warn("Action class [" + className + "] not found");
LOG.debug("Action class [" + className + "] not found", e);
} catch (NoSuchMethodException e) {
LOG.debug("No constructor found for action [{}]", className, e);
throw new ConfigurationException("Action class [" + className + "] does not have a public no-arg constructor", e, loc);
} catch (RuntimeException ex) {
// Probably not a big deal, like request or session-scoped Spring beans that need a real request
@@ -527,10 +545,8 @@ public abstract class XmlDocConfigurationProvider implements ConfigurationProvid
LOG.debug("Action verification cause", ex);
} catch (Exception ex) {
// Default to failing fast
LOG.debug("Unable to verify action class [{}]", className, ex);
throw new ConfigurationException(ex, loc);
throw new ConfigurationException("Unable to verify action class [" + className + "]", ex, loc);
}
return true;
}
protected void addResultTypes(PackageConfig.Builder packageContext, Element element) {
@@ -541,9 +557,7 @@ public abstract class XmlDocConfigurationProvider implements ConfigurationProvid
Location loc = DomHelper.getLocationObject(resultTypeElement);
Class<?> clazz = verifyResultType(className, loc);
if (clazz == null) {
return;
}
String paramName = null;
try {
paramName = (String) clazz.getField("DEFAULT_PARAM").get(null);
@@ -572,11 +586,10 @@ public abstract class XmlDocConfigurationProvider implements ConfigurationProvid
protected Class<?> verifyResultType(String className, Location loc) {
try {
return loadClass(className);
return allowAndLoadClass(className);
} catch (ClassNotFoundException | NoClassDefFoundError e) {
LOG.warn("Result class [{}] doesn't exist ({}) at {}, ignoring", className, e.getClass().getSimpleName(), loc, e);
throw new ConfigurationException("Result class [" + className + "] not found", e, loc);
}
return null;
}
/**
@@ -888,7 +901,12 @@ public abstract class XmlDocConfigurationProvider implements ConfigurationProvid
NodeList defaultClassRefList = element.getElementsByTagName("default-class-ref");
if (defaultClassRefList.getLength() > 0) {
Element defaultClassRefElement = (Element) defaultClassRefList.item(0);
packageContext.defaultClassRef(defaultClassRefElement.getAttribute("class"));
String className = defaultClassRefElement.getAttribute("class");
Location location = DomHelper.getLocationObject(defaultClassRefElement);
verifyAction(className, location);
packageContext.defaultClassRef(className);
}
}
@@ -927,10 +945,26 @@ public abstract class XmlDocConfigurationProvider implements ConfigurationProvid
iterateChildrenByTagName(
element,
"interceptor",
interceptorElement -> context.addInterceptorConfig(buildInterceptorConfig(interceptorElement)));
interceptorElement -> {
String className = interceptorElement.getAttribute("class");
Location location = DomHelper.getLocationObject(interceptorElement);
verifyInterceptor(className, location);
context.addInterceptorConfig(buildInterceptorConfig(interceptorElement));
});
loadInterceptorStacks(element, context);
}
protected void verifyInterceptor(String className, Location loc) {
try {
allowAndLoadClass(className);
} catch (ClassNotFoundException | NoClassDefFoundError e) {
LOG.warn("Interceptor class [" + className + "] at location " + loc + " not found");
LOG.debug("Interceptor class [" + className + "] not found", e);
}
}
protected InterceptorConfig buildInterceptorConfig(Element interceptorElement) {
String interceptorName = interceptorElement.getAttribute("name");
String className = interceptorElement.getAttribute("class");
@@ -863,6 +863,7 @@ public class OgnlUtil {
}
SecurityMemberAccess memberAccess = container.getInstance(SecurityMemberAccess.class);
memberAccess.useEnforceAllowlistEnabled(Boolean.FALSE.toString());
if (devMode) {
if (!warnReported.get()) {
@@ -25,6 +25,7 @@ import org.apache.commons.lang3.BooleanUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.StrutsConstants;
import org.apache.struts2.ognl.ProviderAllowlist;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
@@ -39,6 +40,7 @@ import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static com.opensymphony.xwork2.util.ConfigParseUtil.toClassObjectsSet;
import static com.opensymphony.xwork2.util.ConfigParseUtil.toClassesSet;
import static com.opensymphony.xwork2.util.ConfigParseUtil.toNewClassesSet;
import static com.opensymphony.xwork2.util.ConfigParseUtil.toNewPackageNamesSet;
@@ -57,6 +59,19 @@ public class SecurityMemberAccess implements MemberAccess {
private static final Logger LOG = LogManager.getLogger(SecurityMemberAccess.class);
private static final Set<String> ALLOWLIST_REQUIRED_PACKAGES = unmodifiableSet(new HashSet<>(Arrays.asList(
"org.apache.struts2.components",
"org.apache.struts2.views.jsp",
"com.opensymphony.xwork2.validator.validators"
)));
private static final Set<Class<?>> ALLOWLIST_REQUIRED_CLASSES = unmodifiableSet(new HashSet<>(Arrays.asList(
java.lang.Enum.class,
java.util.Date.class,
java.util.HashMap.class
)));
private final ProviderAllowlist providerAllowlist;
private boolean allowStaticFieldAccess = true;
private Set<Pattern> excludeProperties = emptySet();
private Set<Pattern> acceptProperties = emptySet();
@@ -65,12 +80,14 @@ public class SecurityMemberAccess implements MemberAccess {
private Set<String> excludedPackageNames = emptySet();
private Set<String> excludedPackageExemptClasses = emptySet();
private boolean enforceAllowlistEnabled = false;
private Set<String> allowlistClasses = emptySet();
private Set<Class<?>> allowlistClasses = emptySet();
private Set<String> allowlistPackageNames = emptySet();
private boolean disallowProxyMemberAccess = false;
private boolean disallowDefaultPackageAccess = false;
public SecurityMemberAccess() {
@Inject
public SecurityMemberAccess(@Inject ProviderAllowlist providerAllowlist) {
this.providerAllowlist = providerAllowlist;
}
/**
@@ -79,10 +96,11 @@ public class SecurityMemberAccess implements MemberAccess {
* - 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 since 6.4.0, use {@link #SecurityMemberAccess(ProviderAllowlist)} instead.
*/
@Deprecated
public SecurityMemberAccess(boolean allowStaticFieldAccess) {
this(null);
useAllowStaticFieldAccess(String.valueOf(allowStaticFieldAccess));
}
@@ -199,7 +217,11 @@ public class SecurityMemberAccess implements MemberAccess {
}
protected boolean isClassAllowlisted(Class<?> clazz) {
return allowlistClasses.contains(clazz.getName()) || isClassBelongsToPackages(clazz, allowlistPackageNames);
return allowlistClasses.contains(clazz)
|| ALLOWLIST_REQUIRED_CLASSES.contains(clazz)
|| (providerAllowlist != null && providerAllowlist.getProviderAllowlist().contains(clazz))
|| isClassBelongsToPackages(clazz, ALLOWLIST_REQUIRED_PACKAGES)
|| isClassBelongsToPackages(clazz, allowlistPackageNames);
}
/**
@@ -411,7 +433,7 @@ public class SecurityMemberAccess implements MemberAccess {
@Inject(value = StrutsConstants.STRUTS_ALLOWLIST_CLASSES, required = false)
public void useAllowlistClasses(String commaDelimitedClasses) {
this.allowlistClasses = toClassesSet(commaDelimitedClasses);
this.allowlistClasses = toClassObjectsSet(commaDelimitedClasses);
}
@Inject(value = StrutsConstants.STRUTS_ALLOWLIST_PACKAGE_NAMES, required = false)
@@ -43,6 +43,11 @@ public class ConfigParseUtil {
return unmodifiableSet(classNames);
}
public static Set<Class<?>> toClassObjectsSet(String newDelimitedClasses) throws ConfigurationException {
Set<String> classNames = commaDelimitedStringToSet(newDelimitedClasses);
return unmodifiableSet(validateClasses(classNames, OgnlUtil.class.getClassLoader()));
}
public static Set<String> toNewClassesSet(Set<String> oldClasses, String newDelimitedClasses) throws ConfigurationException {
Set<String> classNames = commaDelimitedStringToSet(newDelimitedClasses);
validateClasses(classNames, OgnlUtil.class.getClassLoader());
@@ -64,14 +69,16 @@ public class ConfigParseUtil {
return unmodifiableSet(newPatterns);
}
public static void validateClasses(Set<String> classNames, ClassLoader validatingClassLoader) throws ConfigurationException {
public static Set<Class<?>> validateClasses(Set<String> classNames, ClassLoader validatingClassLoader) throws ConfigurationException {
Set<Class<?>> classes = new HashSet<>();
for (String className : classNames) {
try {
validatingClassLoader.loadClass(className);
classes.add(validatingClassLoader.loadClass(className));
} catch (ClassNotFoundException e) {
throw new ConfigurationException("Cannot load class for exclusion/exemption configuration: " + className, e);
}
}
return classes;
}
public static Set<String> toPackageNamesSet(String newDelimitedPackageNames) throws ConfigurationException {
@@ -0,0 +1,73 @@
/*
* 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 org.apache.struts2.ognl;
import com.opensymphony.xwork2.config.ConfigurationProvider;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import static java.util.Collections.unmodifiableSet;
/**
* Allows {@link ConfigurationProvider}s to register classes that should be allowed to be used in OGNL expressions.
*
* @since 6.4.0
*/
public class ProviderAllowlist {
private final Map<ConfigurationProvider, Set<Class<?>>> allowlistMap;
private Set<Class<?>> allowlistClasses;
public ProviderAllowlist() {
allowlistMap = new HashMap<>();
reconstructAllowlist();
}
public synchronized void registerAllowlist(ConfigurationProvider configurationProvider, Set<Class<?>> allowlist) {
Set<Class<?>> existingAllowlist = allowlistMap.get(configurationProvider);
if (existingAllowlist != null) {
clearAllowlist(configurationProvider);
}
this.allowlistMap.put(configurationProvider, new HashSet<>(allowlist));
this.allowlistClasses.addAll(allowlist);
}
public synchronized void clearAllowlist(ConfigurationProvider configurationProvider) {
Set<Class<?>> allowlist = allowlistMap.get(configurationProvider);
if (allowlist == null) {
return;
}
this.allowlistMap.remove(configurationProvider);
reconstructAllowlist();
}
public Set<Class<?>> getProviderAllowlist() {
return unmodifiableSet(allowlistClasses);
}
private void reconstructAllowlist() {
this.allowlistClasses = allowlistMap.values().stream().reduce(new HashSet<>(), (a, b) -> {
a.addAll(b);
return a;
});
}
}
+1
View File
@@ -169,6 +169,7 @@
<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"/>
<bean class="org.apache.struts2.ognl.ProviderAllowlist"/>
<bean type="com.opensymphony.xwork2.util.TextParser" name="struts"
class="com.opensymphony.xwork2.util.OgnlTextParser" scope="singleton"/>
@@ -44,8 +44,6 @@
<interceptors>
<interceptor name="alias" class="com.opensymphony.xwork2.interceptor.AliasInterceptor"/>
<interceptor name="autowiring"
class="com.opensymphony.xwork2.spring.interceptor.ActionAutowiringInterceptor"/>
<interceptor name="chain" class="com.opensymphony.xwork2.interceptor.ChainingInterceptor"/>
<interceptor name="coep" class="org.apache.struts2.interceptor.CoepInterceptor"/>
<interceptor name="conversionError"
@@ -43,7 +43,8 @@
java.lang.System,
java.lang.Thread,
java.lang.ThreadGroup,
java.lang.ThreadLocal"/>
java.lang.ThreadLocal
"/>
<constant name="struts.devMode.excludedClasses"
value="
@@ -61,7 +62,8 @@
java.lang.System,
java.lang.Thread,
java.lang.ThreadGroup,
java.lang.ThreadLocal"/>
java.lang.ThreadLocal
"/>
<!-- this must be valid regex, each '.' in package name must be escaped! -->
<!-- it's more flexible but slower than simple string comparison -->
@@ -98,7 +100,8 @@
org.wildfly.extension.undertow.deployment,
org.yaml.snakeyaml,
sun.misc,
sun.reflect"/>
sun.reflect
"/>
<constant name="struts.devMode.excludedPackageNames"
value="
@@ -129,6 +132,7 @@
org.wildfly.extension.undertow.deployment,
org.yaml.snakeyaml,
sun.misc,
sun.reflect"/>
sun.reflect
"/>
</struts>
@@ -0,0 +1,114 @@
/*
* 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.config.providers;
import com.opensymphony.xwork2.XWorkJUnit4TestCase;
import com.opensymphony.xwork2.config.ConfigurationProvider;
import org.apache.struts2.config.StrutsXmlConfigurationProvider;
import org.apache.struts2.ognl.ProviderAllowlist;
import org.junit.Before;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class ConfigurationProviderOgnlAllowlistTest extends XWorkJUnit4TestCase {
private final ConfigurationProvider testXml1 = new StrutsXmlConfigurationProvider("com/opensymphony/xwork2/config/providers/xwork-test-allowlist.xml");
private final ConfigurationProvider testXml2 = new StrutsXmlConfigurationProvider("com/opensymphony/xwork2/config/providers/xwork-test-allowlist-2.xml");
private ProviderAllowlist providerAllowlist;
@Before
public void setUp() throws Exception {
loadConfigurationProviders(testXml1, testXml2);
providerAllowlist = container.getInstance(ProviderAllowlist.class);
}
@Test
public void allowlist() throws Exception {
loadConfigurationProviders(testXml1, testXml2);
providerAllowlist = container.getInstance(ProviderAllowlist.class);
assertThat(providerAllowlist.getProviderAllowlist()).containsExactlyInAnyOrder(
Class.forName("com.opensymphony.xwork2.interceptor.ValidationAware"),
Class.forName("com.opensymphony.xwork2.LocaleProvider"),
Class.forName("java.io.Serializable"),
Class.forName("com.opensymphony.xwork2.mock.MockResult"),
Class.forName("com.opensymphony.xwork2.interceptor.ConditionalInterceptor"),
Class.forName("com.opensymphony.xwork2.ActionSupport"),
Class.forName("com.opensymphony.xwork2.ActionChainResult"),
Class.forName("com.opensymphony.xwork2.TextProvider"),
Class.forName("org.apache.struts2.interceptor.NoOpInterceptor"),
Class.forName("com.opensymphony.xwork2.interceptor.Interceptor"),
Class.forName("java.lang.Object"),
Class.forName("com.opensymphony.xwork2.Validateable"),
Class.forName("com.opensymphony.xwork2.mock.MockInterceptor"),
Class.forName("com.opensymphony.xwork2.Action"),
Class.forName("com.opensymphony.xwork2.interceptor.AbstractInterceptor"),
Class.forName("com.opensymphony.xwork2.Result"),
Class.forName("com.opensymphony.xwork2.SimpleAction")
);
}
@Test
public void allowlist_1only() throws Exception {
loadConfigurationProviders(testXml1);
providerAllowlist = container.getInstance(ProviderAllowlist.class);
assertThat(providerAllowlist.getProviderAllowlist()).containsExactlyInAnyOrder(
Class.forName("com.opensymphony.xwork2.interceptor.ValidationAware"),
Class.forName("com.opensymphony.xwork2.LocaleProvider"),
Class.forName("java.io.Serializable"),
Class.forName("com.opensymphony.xwork2.mock.MockResult"),
Class.forName("com.opensymphony.xwork2.interceptor.ConditionalInterceptor"),
Class.forName("com.opensymphony.xwork2.ActionSupport"),
Class.forName("com.opensymphony.xwork2.TextProvider"),
Class.forName("com.opensymphony.xwork2.interceptor.Interceptor"),
Class.forName("java.lang.Object"),
Class.forName("com.opensymphony.xwork2.Validateable"),
Class.forName("com.opensymphony.xwork2.mock.MockInterceptor"),
Class.forName("com.opensymphony.xwork2.Action"),
Class.forName("com.opensymphony.xwork2.interceptor.AbstractInterceptor"),
Class.forName("com.opensymphony.xwork2.Result"),
Class.forName("com.opensymphony.xwork2.SimpleAction")
);
}
@Test
public void allowlist_2only() throws Exception {
loadConfigurationProviders(testXml2);
providerAllowlist = container.getInstance(ProviderAllowlist.class);
assertThat(providerAllowlist.getProviderAllowlist()).containsExactlyInAnyOrder(
Class.forName("com.opensymphony.xwork2.interceptor.ValidationAware"),
Class.forName("com.opensymphony.xwork2.LocaleProvider"),
Class.forName("java.io.Serializable"),
Class.forName("com.opensymphony.xwork2.interceptor.ConditionalInterceptor"),
Class.forName("com.opensymphony.xwork2.ActionSupport"),
Class.forName("com.opensymphony.xwork2.ActionChainResult"),
Class.forName("com.opensymphony.xwork2.TextProvider"),
Class.forName("org.apache.struts2.interceptor.NoOpInterceptor"),
Class.forName("com.opensymphony.xwork2.interceptor.Interceptor"),
Class.forName("java.lang.Object"),
Class.forName("com.opensymphony.xwork2.Validateable"),
Class.forName("com.opensymphony.xwork2.Action"),
Class.forName("com.opensymphony.xwork2.interceptor.AbstractInterceptor"),
Class.forName("com.opensymphony.xwork2.Result")
);
}
}
@@ -24,6 +24,7 @@ import com.opensymphony.xwork2.test.TestBean2;
import com.opensymphony.xwork2.util.Foo;
import ognl.MemberAccess;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.struts2.ognl.ProviderAllowlist;
import org.junit.Before;
import org.junit.Test;
@@ -44,22 +45,28 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class SecurityMemberAccessTest {
private Map context;
private FooBar target;
protected SecurityMemberAccess sma;
private ProviderAllowlist mockedProviderAllowlist;
@Before
public void setUp() throws Exception {
context = new HashMap<>();
target = new FooBar();
mockedProviderAllowlist = mock(ProviderAllowlist.class);
assignNewSma(true);
}
protected void assignNewSma(boolean allowStaticFieldAccess) {
sma = new SecurityMemberAccess(allowStaticFieldAccess);
when(mockedProviderAllowlist.getProviderAllowlist()).thenReturn(new HashSet<>());
sma = new SecurityMemberAccess(mockedProviderAllowlist);
sma.useAllowStaticFieldAccess(String.valueOf(allowStaticFieldAccess));
}
private <T> T reflectField(String fieldName) throws IllegalAccessException {
@@ -0,0 +1,88 @@
/*
* 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 org.apache.struts2.ognl;
import com.opensymphony.xwork2.config.ConfigurationProvider;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import java.util.HashSet;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
public class ProviderAllowlistTest {
@Rule
public final MockitoRule mockito = MockitoJUnit.rule();
private ProviderAllowlist providerAllowlist;
@Mock
private ConfigurationProvider provider1;
@Mock
private ConfigurationProvider provider2;
@Before
public void setUp() throws Exception {
providerAllowlist = new ProviderAllowlist();
}
@Test
public void registerAllowlist() {
providerAllowlist.registerAllowlist(provider1, new HashSet<>(asList(String.class, Integer.class)));
providerAllowlist.registerAllowlist(provider2, new HashSet<>(asList(Double.class, Integer.class)));
assertThat(providerAllowlist.getProviderAllowlist()).containsExactlyInAnyOrder(String.class, Integer.class, Double.class);
}
@Test
public void registerAllowlist_twice() {
providerAllowlist.registerAllowlist(provider1, new HashSet<>(asList(String.class, Integer.class)));
providerAllowlist.registerAllowlist(provider1, new HashSet<>(asList(Double.class, Integer.class)));
assertThat(providerAllowlist.getProviderAllowlist()).containsExactlyInAnyOrder(Integer.class, Double.class);
}
@Test
public void clearAllowlist() {
providerAllowlist.registerAllowlist(provider1, new HashSet<>(asList(String.class, Integer.class)));
providerAllowlist.registerAllowlist(provider2, new HashSet<>(asList(Double.class, Integer.class)));
providerAllowlist.clearAllowlist(provider1);
assertThat(providerAllowlist.getProviderAllowlist()).containsExactlyInAnyOrder(Integer.class, Double.class);
}
@Test
public void clearAllowlist_both() {
providerAllowlist.registerAllowlist(provider1, new HashSet<>(asList(String.class, Integer.class)));
providerAllowlist.registerAllowlist(provider2, new HashSet<>(asList(Double.class, Integer.class)));
providerAllowlist.clearAllowlist(provider1);
providerAllowlist.clearAllowlist(provider2);
assertThat(providerAllowlist.getProviderAllowlist()).isEmpty();
}
}
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
/*
* 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.
*/
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 6.0//EN"
"https://struts.apache.org/dtds/struts-6.0.dtd">
<struts>
<package name="allow2">
<result-types>
<result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult" default="true"/>
</result-types>
<interceptors>
<interceptor name="noop" class="org.apache.struts2.interceptor.NoOpInterceptor"/>
</interceptors>
<action name="WildCard" class="com.opensymphony.xwork2.ActionSupport">
<result name="*" type="chain"/>
<interceptor-ref name="noop"/>
</action>
</package>
</struts>
@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
/*
* 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.
*/
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 6.0//EN"
"https://struts.apache.org/dtds/struts-6.0.dtd">
<struts>
<package name="allow">
<result-types>
<result-type name="mock" class="com.opensymphony.xwork2.mock.MockResult"/>
</result-types>
<interceptors>
<interceptor name="test" class="com.opensymphony.xwork2.mock.MockInterceptor">
<param name="foo">fooDefault</param>
</interceptor>
<interceptor-stack name="defaultStack">
<interceptor-ref name="test"/>
</interceptor-stack>
</interceptors>
<action name="Foo" class="com.opensymphony.xwork2.SimpleAction">
<param name="foo">18</param>
<param name="bar">24</param>
<result name="success" type="mock"/>
<interceptor-ref name="defaultStack"/>
</action>
</package>
</struts>
@@ -21,7 +21,6 @@ package org.apache.struts2.junit;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionProxy;
import com.opensymphony.xwork2.ActionProxyFactory;
import com.opensymphony.xwork2.XWorkTestCase;
import com.opensymphony.xwork2.config.Configuration;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.dispatcher.Dispatcher;
@@ -18,74 +18,5 @@
*/
package org.apache.struts2.junit;
import com.opensymphony.xwork2.ActionProxyFactory;
import com.opensymphony.xwork2.config.Configuration;
import com.opensymphony.xwork2.config.ConfigurationException;
import com.opensymphony.xwork2.config.ConfigurationManager;
import com.opensymphony.xwork2.config.ConfigurationProvider;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.inject.ContainerBuilder;
import com.opensymphony.xwork2.inject.Context;
import com.opensymphony.xwork2.inject.Factory;
import com.opensymphony.xwork2.inject.Scope;
import com.opensymphony.xwork2.test.StubConfigurationProvider;
import com.opensymphony.xwork2.util.XWorkTestCaseHelper;
import com.opensymphony.xwork2.util.location.LocatableProperties;
import org.junit.After;
import org.junit.Before;
public abstract class XWorkJUnit4TestCase {
protected ConfigurationManager configurationManager;
protected Configuration configuration;
protected Container container;
protected ActionProxyFactory actionProxyFactory;
@Before
public void setUp() throws Exception {
configurationManager = XWorkTestCaseHelper.setUp();
configuration = configurationManager.getConfiguration();
container = configuration.getContainer();
actionProxyFactory = container.getInstance(ActionProxyFactory.class);
}
@After
public void tearDown() throws Exception {
XWorkTestCaseHelper.tearDown(configurationManager);
configurationManager = null;
configuration = null;
container = null;
actionProxyFactory = null;
}
protected void loadConfigurationProviders(ConfigurationProvider... providers) {
configurationManager = XWorkTestCaseHelper.loadConfigurationProviders(configurationManager, providers);
configuration = configurationManager.getConfiguration();
container = configuration.getContainer();
actionProxyFactory = container.getInstance(ActionProxyFactory.class);
}
protected void loadButAdd(final Class<?> type, final Object impl) {
loadButAdd(type, Container.DEFAULT_NAME, impl);
}
protected void loadButAdd(final Class<?> type, final String name, final Object impl) {
loadConfigurationProviders(new StubConfigurationProvider() {
@Override
public void register(ContainerBuilder builder,
LocatableProperties props) throws ConfigurationException {
builder.factory(type, name, new Factory() {
public Object create(Context context) throws Exception {
return impl;
}
@Override
public Class type() {
return impl.getClass();
}
}, Scope.SINGLETON);
}
});
}
public abstract class XWorkJUnit4TestCase extends com.opensymphony.xwork2.XWorkJUnit4TestCase {
}
@@ -0,0 +1,22 @@
/*
* 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 org.apache.struts2.junit;
public abstract class XWorkTestCase extends com.opensymphony.xwork2.XWorkTestCase {
}