From 45b8c68a3804351cd46d8437e173527e2321b74e Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: May 20 2020 10:00:16 +0000 Subject: import apache-commons-beanutils-1.8.3-15.el7_7 --- diff --git a/SOURCES/CVE-2014-0114.patch b/SOURCES/CVE-2014-0114.patch new file mode 100644 index 0000000..890f5c8 --- /dev/null +++ b/SOURCES/CVE-2014-0114.patch @@ -0,0 +1,1263 @@ +From fd220124926ff0c6e760d5331eb62e09b20b46a9 Mon Sep 17 00:00:00 2001 +From: Marian Koncek +Date: Wed, 18 Dec 2019 16:01:16 +0100 +Subject: [PATCH] CVE-2014-0114 + +--- + .../beanutils/BeanIntrospectionData.java | 154 +++++++++++++ + .../commons/beanutils/BeanIntrospector.java | 53 +++++ + .../beanutils/DefaultBeanIntrospector.java | 181 +++++++++++++++ + .../DefaultIntrospectionContext.java | 108 +++++++++ + .../beanutils/IntrospectionContext.java | 99 ++++++++ + .../commons/beanutils/PropertyUtils.java | 15 ++ + .../commons/beanutils/PropertyUtilsBean.java | 213 +++++++++--------- + .../SuppressPropertiesBeanIntrospector.java | 75 ++++++ + ...essPropertiesBeanIntrospectorTestCase.java | 127 +++++++++++ + .../beanutils/bugs/Jira463TestCase.java | 48 ++++ + .../beanutils/bugs/Jira520TestCase.java | 39 ++++ + 11 files changed, 1000 insertions(+), 112 deletions(-) + create mode 100644 src/main/java/org/apache/commons/beanutils/BeanIntrospectionData.java + create mode 100644 src/main/java/org/apache/commons/beanutils/BeanIntrospector.java + create mode 100644 src/main/java/org/apache/commons/beanutils/DefaultBeanIntrospector.java + create mode 100644 src/main/java/org/apache/commons/beanutils/DefaultIntrospectionContext.java + create mode 100644 src/main/java/org/apache/commons/beanutils/IntrospectionContext.java + create mode 100644 src/main/java/org/apache/commons/beanutils/SuppressPropertiesBeanIntrospector.java + create mode 100644 src/test/java/org/apache/commons/beanutils/SuppressPropertiesBeanIntrospectorTestCase.java + create mode 100644 src/test/java/org/apache/commons/beanutils/bugs/Jira463TestCase.java + create mode 100644 src/test/java/org/apache/commons/beanutils/bugs/Jira520TestCase.java + +diff --git a/src/main/java/org/apache/commons/beanutils/BeanIntrospectionData.java b/src/main/java/org/apache/commons/beanutils/BeanIntrospectionData.java +new file mode 100644 +index 0000000..1ab1d1e +--- /dev/null ++++ b/src/main/java/org/apache/commons/beanutils/BeanIntrospectionData.java +@@ -0,0 +1,154 @@ ++/* ++ * 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.commons.beanutils; ++ ++import java.beans.IntrospectionException; ++import java.beans.PropertyDescriptor; ++import java.lang.reflect.Method; ++import java.util.HashMap; ++import java.util.Map; ++ ++/** ++ *

++ * An internally used helper class for storing introspection information about a bean ++ * class. ++ *

++ *

++ * This class is used by {@link PropertyUtilsBean}. When accessing bean properties via ++ * reflection information about the properties available and their types and access ++ * methods must be present. {@code PropertyUtilsBean} stores this information in a cache ++ * so that it can be accessed quickly. The cache stores instances of this class. ++ *

++ *

++ * This class mainly stores information about the properties of a bean class. Per default, ++ * this is contained in {@code PropertyDescriptor} objects. Some additional information ++ * required by the {@code BeanUtils} library is also stored here. ++ *

++ * ++ * @version $Id$ ++ * @since 1.9.1 ++ */ ++class BeanIntrospectionData { ++ /** An array with property descriptors for the managed bean class. */ ++ private final PropertyDescriptor[] descriptors; ++ ++ /** A map for remembering the write method names for properties. */ ++ private final Map writeMethodNames; ++ ++ /** ++ * Creates a new instance of {@code BeanIntrospectionData} and initializes its ++ * completely. ++ * ++ * @param descs the array with the descriptors of the available properties ++ */ ++ public BeanIntrospectionData(final PropertyDescriptor[] descs) { ++ this(descs, setUpWriteMethodNames(descs)); ++ } ++ ++ /** ++ * Creates a new instance of {@code BeanIntrospectionData} and allows setting the map ++ * with write method names. This constructor is mainly used for testing purposes. ++ * ++ * @param descs the array with the descriptors of the available properties ++ * @param writeMethNames the map with the names of write methods ++ */ ++ BeanIntrospectionData(final PropertyDescriptor[] descs, final Map writeMethNames) { ++ descriptors = descs; ++ writeMethodNames = writeMethNames; ++ } ++ ++ /** ++ * Returns the array with property descriptors. ++ * ++ * @return the property descriptors for the associated bean class ++ */ ++ public PropertyDescriptor[] getDescriptors() { ++ return descriptors; ++ } ++ ++ /** ++ * Returns the {@code PropertyDescriptor} for the property with the specified name. If ++ * this property is unknown, result is null. ++ * ++ * @param name the name of the property in question ++ * @return the {@code PropertyDescriptor} for this property or null ++ */ ++ public PropertyDescriptor getDescriptor(final String name) { ++ PropertyDescriptor[] descriptors = getDescriptors(); ++ for (int i = 0; i < descriptors.length; ++i) { ++ PropertyDescriptor pd = descriptors[i]; ++ if (name.equals(pd.getName())) { ++ return pd; ++ } ++ } ++ return null; ++ } ++ ++ /** ++ * Returns the write method for the property determined by the given ++ * {@code PropertyDescriptor}. This information is normally available in the ++ * descriptor object itself. However, at least by the ORACLE implementation, the ++ * method is stored as a {@code SoftReference}. If this reference has been freed by ++ * the GC, it may be the case that the method cannot be obtained again. Then, ++ * additional information stored in this object is necessary to obtain the method ++ * again. ++ * ++ * @param beanCls the class of the affected bean ++ * @param desc the {@code PropertyDescriptor} of the desired property ++ * @return the write method for this property or null if there is none ++ */ ++ public Method getWriteMethod(final Class beanCls, final PropertyDescriptor desc) { ++ Method method = desc.getWriteMethod(); ++ if (method == null) { ++ final String methodName = (String) writeMethodNames.get(desc.getName()); ++ if (methodName != null) { ++ method = MethodUtils.getAccessibleMethod(beanCls, methodName, ++ desc.getPropertyType()); ++ if (method != null) { ++ try { ++ desc.setWriteMethod(method); ++ } catch (final IntrospectionException e) { ++ // ignore, in this case the method is not cached ++ } ++ } ++ } ++ } ++ ++ return method; ++ } ++ ++ /** ++ * Initializes the map with the names of the write methods for the supported ++ * properties. The method names - if defined - need to be stored separately because ++ * they may get lost when the GC claims soft references used by the ++ * {@code PropertyDescriptor} objects. ++ * ++ * @param descs the array with the descriptors of the available properties ++ * @return the map with the names of write methods for properties ++ */ ++ private static Map setUpWriteMethodNames(final PropertyDescriptor[] descs) { ++ final Map methods = new HashMap(); ++ for (int i = 0; i < descs.length; ++i) { ++ PropertyDescriptor pd = descs[i]; ++ final Method method = pd.getWriteMethod(); ++ if (method != null) { ++ methods.put(pd.getName(), method.getName()); ++ } ++ } ++ return methods; ++ } ++} +diff --git a/src/main/java/org/apache/commons/beanutils/BeanIntrospector.java b/src/main/java/org/apache/commons/beanutils/BeanIntrospector.java +new file mode 100644 +index 0000000..3f5545e +--- /dev/null ++++ b/src/main/java/org/apache/commons/beanutils/BeanIntrospector.java +@@ -0,0 +1,53 @@ ++/* ++ * 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.commons.beanutils; ++ ++import java.beans.IntrospectionException; ++ ++/** ++ *

++ * Definition of an interface for components that can perform introspection on ++ * bean classes. ++ *

++ *

++ * Before {@link PropertyUtils} can be used for interaction with a specific Java ++ * class, the class's properties have to be determined. This is called ++ * introspection and is initiated automatically on demand. ++ * PropertyUtils does not perform introspection on its own, but ++ * delegates this task to one or more objects implementing this interface. This ++ * makes it possible to customize introspection which may be useful for certain ++ * code bases using non-standard conventions for accessing properties. ++ *

++ * ++ * @version $Id: BeanIntrospector.java 1540359 2013-11-09 18:10:52Z oheger $ ++ * @since 1.9 ++ */ ++public interface BeanIntrospector { ++ /** ++ * Performs introspection on a Java class. The current class to be inspected ++ * can be queried from the passed in IntrospectionContext ++ * object. A typical implementation has to obtain this class, determine its ++ * properties according to the rules it implements, and add them to the ++ * passed in context object. ++ * ++ * @param icontext the context object for interaction with the initiator of ++ * the introspection request ++ * @throws IntrospectionException if an error occurs during introspection ++ */ ++ void introspect(IntrospectionContext icontext) ++ throws IntrospectionException; ++} +diff --git a/src/main/java/org/apache/commons/beanutils/DefaultBeanIntrospector.java b/src/main/java/org/apache/commons/beanutils/DefaultBeanIntrospector.java +new file mode 100644 +index 0000000..60657d0 +--- /dev/null ++++ b/src/main/java/org/apache/commons/beanutils/DefaultBeanIntrospector.java +@@ -0,0 +1,181 @@ ++/* ++ * 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.commons.beanutils; ++ ++import java.beans.BeanInfo; ++import java.beans.IndexedPropertyDescriptor; ++import java.beans.IntrospectionException; ++import java.beans.Introspector; ++import java.beans.PropertyDescriptor; ++import java.lang.reflect.Method; ++import java.util.List; ++ ++import org.apache.commons.logging.Log; ++import org.apache.commons.logging.LogFactory; ++ ++/** ++ *

++ * The default {@link BeanIntrospector} implementation. ++ *

++ *

++ * This class implements a default bean introspection algorithm based on the JDK ++ * classes in the java.beans package. It discovers properties ++ * conforming to the Java Beans specification. ++ *

++ *

++ * This class is a singleton. The single instance can be obtained using the ++ * {@code INSTANCE} field. It does not define any state and thus can be ++ * shared by arbitrary clients. {@link PropertyUtils} per default uses this ++ * instance as its only {@code BeanIntrospector} object. ++ *

++ * ++ * @version $Id$ ++ * @since 1.9 ++ */ ++public class DefaultBeanIntrospector implements BeanIntrospector { ++ /** The singleton instance of this class. */ ++ public static final BeanIntrospector INSTANCE = new DefaultBeanIntrospector(); ++ ++ /** Constant for argument types of a method that expects no arguments. */ ++ private static final Class[] EMPTY_CLASS_PARAMETERS = new Class[0]; ++ ++ /** Constant for arguments types of a method that expects a list argument. */ ++ private static final Class[] LIST_CLASS_PARAMETER = new Class[] { java.util.List.class }; ++ ++ /** Log instance */ ++ private final Log log = LogFactory.getLog(getClass()); ++ ++ /** ++ * Private constructor so that no instances can be created. ++ */ ++ private DefaultBeanIntrospector() { ++ } ++ ++ /** ++ * Performs introspection of a specific Java class. This implementation uses ++ * the {@code java.beans.Introspector.getBeanInfo()} method to obtain ++ * all property descriptors for the current class and adds them to the ++ * passed in introspection context. ++ * ++ * @param icontext the introspection context ++ */ ++ public void introspect(final IntrospectionContext icontext) { ++ BeanInfo beanInfo = null; ++ try { ++ beanInfo = Introspector.getBeanInfo(icontext.getTargetClass()); ++ } catch (final IntrospectionException e) { ++ // no descriptors are added to the context ++ log.error( ++ "Error when inspecting class " + icontext.getTargetClass(), ++ e); ++ return; ++ } ++ ++ PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors(); ++ if (descriptors == null) { ++ descriptors = new PropertyDescriptor[0]; ++ } ++ ++ handleIndexedPropertyDescriptors(icontext.getTargetClass(), ++ descriptors); ++ icontext.addPropertyDescriptors(descriptors); ++ } ++ ++ /** ++ * This method fixes an issue where IndexedPropertyDescriptor behaves ++ * differently in different versions of the JDK for 'indexed' properties ++ * which use java.util.List (rather than an array). It implements a ++ * workaround for Bug 28358. If you have a Bean with the following ++ * getters/setters for an indexed property: ++ * ++ *
++     * public List getFoo()
++     * public Object getFoo(int index)
++     * public void setFoo(List foo)
++     * public void setFoo(int index, Object foo)
++     * 
++ * ++ * then the IndexedPropertyDescriptor's getReadMethod() and getWriteMethod() ++ * behave as follows: ++ * ++ * ++ * @param beanClass the current class to be inspected ++ * @param descriptors the array with property descriptors ++ */ ++ private void handleIndexedPropertyDescriptors(final Class beanClass, ++ final PropertyDescriptor[] descriptors) { ++ for (final PropertyDescriptor pd : descriptors) { ++ if (pd instanceof IndexedPropertyDescriptor) { ++ final IndexedPropertyDescriptor descriptor = (IndexedPropertyDescriptor) pd; ++ final String propName = descriptor.getName().substring(0, 1) ++ .toUpperCase() ++ + descriptor.getName().substring(1); ++ ++ if (descriptor.getReadMethod() == null) { ++ final String methodName = descriptor.getIndexedReadMethod() != null ? descriptor ++ .getIndexedReadMethod().getName() : "get" ++ + propName; ++ final Method readMethod = MethodUtils ++ .getMatchingAccessibleMethod(beanClass, methodName, ++ EMPTY_CLASS_PARAMETERS); ++ if (readMethod != null) { ++ try { ++ descriptor.setReadMethod(readMethod); ++ } catch (final Exception e) { ++ log.error( ++ "Error setting indexed property read method", ++ e); ++ } ++ } ++ } ++ if (descriptor.getWriteMethod() == null) { ++ final String methodName = descriptor.getIndexedWriteMethod() != null ? descriptor ++ .getIndexedWriteMethod().getName() : "set" ++ + propName; ++ Method writeMethod = MethodUtils ++ .getMatchingAccessibleMethod(beanClass, methodName, ++ LIST_CLASS_PARAMETER); ++ if (writeMethod == null) { ++ for (final Method m : beanClass.getMethods()) { ++ if (m.getName().equals(methodName)) { ++ final Class[] parameterTypes = m.getParameterTypes(); ++ if (parameterTypes.length == 1 ++ && List.class ++ .isAssignableFrom(parameterTypes[0])) { ++ writeMethod = m; ++ break; ++ } ++ } ++ } ++ } ++ if (writeMethod != null) { ++ try { ++ descriptor.setWriteMethod(writeMethod); ++ } catch (final Exception e) { ++ log.error( ++ "Error setting indexed property write method", ++ e); ++ } ++ } ++ } ++ } ++ } ++ } ++} +diff --git a/src/main/java/org/apache/commons/beanutils/DefaultIntrospectionContext.java b/src/main/java/org/apache/commons/beanutils/DefaultIntrospectionContext.java +new file mode 100644 +index 0000000..9171198 +--- /dev/null ++++ b/src/main/java/org/apache/commons/beanutils/DefaultIntrospectionContext.java +@@ -0,0 +1,108 @@ ++/* ++ * 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.commons.beanutils; ++ ++import java.beans.PropertyDescriptor; ++import java.util.HashMap; ++import java.util.Map; ++import java.util.Set; ++ ++/** ++ *

++ * An implementation of the {@code IntrospectionContext} interface used by ++ * {@link PropertyUtilsBean} when doing introspection of a bean class. ++ *

++ *

++ * This class implements the methods required by the ++ * {@code IntrospectionContext} interface in a straight-forward manner ++ * based on a map. It is used internally only. It is not thread-safe. ++ *

++ * ++ * @version $Id$ ++ * @since 1.9 ++ */ ++class DefaultIntrospectionContext implements IntrospectionContext { ++ /** Constant for an empty array of property descriptors. */ ++ private static final PropertyDescriptor[] EMPTY_DESCRIPTORS = new PropertyDescriptor[0]; ++ ++ /** The current class for introspection. */ ++ private final Class currentClass; ++ ++ /** A map for storing the already added property descriptors. */ ++ private final Map descriptors; ++ ++ /** ++ * ++ * Creates a new instance of DefaultIntrospectionContext and sets ++ * the current class for introspection. ++ * ++ * @param cls the current class ++ */ ++ public DefaultIntrospectionContext(final Class cls) { ++ currentClass = cls; ++ descriptors = new HashMap(); ++ } ++ ++ public Class getTargetClass() { ++ return currentClass; ++ } ++ ++ public void addPropertyDescriptor(final PropertyDescriptor desc) { ++ if (desc == null) { ++ throw new IllegalArgumentException( ++ "Property descriptor must not be null!"); ++ } ++ descriptors.put(desc.getName(), desc); ++ } ++ ++ public void addPropertyDescriptors(final PropertyDescriptor[] descs) { ++ if (descs == null) { ++ throw new IllegalArgumentException( ++ "Array with descriptors must not be null!"); ++ } ++ ++ for (int i = 0; i < descs.length; ++i) { ++ addPropertyDescriptor(descs[i]); ++ } ++ } ++ ++ public boolean hasProperty(final String name) { ++ return descriptors.containsKey(name); ++ } ++ ++ public PropertyDescriptor getPropertyDescriptor(final String name) { ++ return (PropertyDescriptor) descriptors.get(name); ++ } ++ ++ public void removePropertyDescriptor(final String name) { ++ descriptors.remove(name); ++ } ++ ++ public Set propertyNames() { ++ return descriptors.keySet(); ++ } ++ ++ /** ++ * Returns an array with all descriptors added to this context. This method ++ * is used to obtain the results of introspection. ++ * ++ * @return an array with all known property descriptors ++ */ ++ public PropertyDescriptor[] getPropertyDescriptors() { ++ return (PropertyDescriptor[]) descriptors.values().toArray(EMPTY_DESCRIPTORS); ++ } ++} +diff --git a/src/main/java/org/apache/commons/beanutils/IntrospectionContext.java b/src/main/java/org/apache/commons/beanutils/IntrospectionContext.java +new file mode 100644 +index 0000000..c48f186 +--- /dev/null ++++ b/src/main/java/org/apache/commons/beanutils/IntrospectionContext.java +@@ -0,0 +1,99 @@ ++/* ++ * 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.commons.beanutils; ++ ++import java.beans.PropertyDescriptor; ++import java.util.Set; ++ ++/** ++ *

++ * A context interface used during introspection for querying and setting ++ * property descriptors. ++ *

++ *

++ * An implementation of this interface is passed to {@link BeanIntrospector} ++ * objects during processing of a bean class. It allows the ++ * {@code BeanIntrospector} to deliver descriptors for properties it has ++ * detected. It is also possible to find out which properties have already been ++ * found by another {@code BeanIntrospector}; this allows multiple ++ * {@code BeanIntrospector} instances to collaborate. ++ *

++ * ++ * @version $Id: IntrospectionContext.java 1540359 2013-11-09 18:10:52Z oheger $ ++ * @since 1.9 ++ */ ++public interface IntrospectionContext { ++ /** ++ * Returns the class that is subject of introspection. ++ * ++ * @return the current class ++ */ ++ Class getTargetClass(); ++ ++ /** ++ * Adds the given property descriptor to this context. This method is called ++ * by a {@code BeanIntrospector} during introspection for each detected ++ * property. If this context already contains a descriptor for the affected ++ * property, it is overridden. ++ * ++ * @param desc the property descriptor ++ */ ++ void addPropertyDescriptor(PropertyDescriptor desc); ++ ++ /** ++ * Adds an array of property descriptors to this context. Using this method ++ * multiple descriptors can be added at once. ++ * ++ * @param descriptors the array of descriptors to be added ++ */ ++ void addPropertyDescriptors(PropertyDescriptor[] descriptors); ++ ++ /** ++ * Tests whether a descriptor for the property with the given name is ++ * already contained in this context. This method can be used for instance ++ * to prevent that an already existing property descriptor is overridden. ++ * ++ * @param name the name of the property in question ++ * @return true if a descriptor for this property has already been ++ * added, false otherwise ++ */ ++ boolean hasProperty(String name); ++ ++ /** ++ * Returns the descriptor for the property with the given name or ++ * null if this property is unknown. ++ * ++ * @param name the name of the property in question ++ * @return the descriptor for this property or null if this property ++ * is unknown ++ */ ++ PropertyDescriptor getPropertyDescriptor(String name); ++ ++ /** ++ * Removes the descriptor for the property with the given name. ++ * ++ * @param name the name of the affected property ++ */ ++ void removePropertyDescriptor(String name); ++ ++ /** ++ * Returns a set with the names of all properties known to this context. ++ * ++ * @return a set with the known property names ++ */ ++ Set propertyNames(); ++} +diff --git a/src/main/java/org/apache/commons/beanutils/PropertyUtils.java b/src/main/java/org/apache/commons/beanutils/PropertyUtils.java +index 6344b4f..b3ce0e6 100644 +--- a/src/main/java/org/apache/commons/beanutils/PropertyUtils.java ++++ b/src/main/java/org/apache/commons/beanutils/PropertyUtils.java +@@ -890,5 +890,20 @@ public class PropertyUtils { + PropertyUtilsBean.getInstance().setSimpleProperty(bean, name, value); + } + ++ public static void addBeanIntrospector(final BeanIntrospector introspector) { ++ PropertyUtilsBean.getInstance().addBeanIntrospector(introspector); ++ } + ++ /** ++ * Removes the specified BeanIntrospector. ++ * ++ * @param introspector the BeanIntrospector to be removed ++ * @return true if the BeanIntrospector existed and ++ * could be removed, false otherwise ++ * @since 1.9 ++ */ ++ public static boolean removeBeanIntrospector(final BeanIntrospector introspector) { ++ return PropertyUtilsBean.getInstance().removeBeanIntrospector( ++ introspector); ++ } + } +diff --git a/src/main/java/org/apache/commons/beanutils/PropertyUtilsBean.java b/src/main/java/org/apache/commons/beanutils/PropertyUtilsBean.java +index 1894f21..c7817cd 100644 +--- a/src/main/java/org/apache/commons/beanutils/PropertyUtilsBean.java ++++ b/src/main/java/org/apache/commons/beanutils/PropertyUtilsBean.java +@@ -37,6 +37,7 @@ import org.apache.commons.collections.FastHashMap; + import org.apache.commons.logging.Log; + import org.apache.commons.logging.LogFactory; + ++import java.util.concurrent.CopyOnWriteArrayList; + + /** + * Utility methods for using Java Reflection APIs to facilitate generic +@@ -130,7 +131,9 @@ public class PropertyUtilsBean { + + /** Log instance */ + private Log log = LogFactory.getLog(PropertyUtils.class); +- ++ ++ /** The list with BeanIntrospector objects. */ ++ private final List introspectors; + // ---------------------------------------------------------- Constructors + + /** Base constructor */ +@@ -139,6 +142,8 @@ public class PropertyUtilsBean { + descriptorsCache.setFast(true); + mappedDescriptorsCache = new WeakFastHashMap(); + mappedDescriptorsCache.setFast(true); ++ introspectors = new CopyOnWriteArrayList(); ++ resetBeanIntrospectors(); + } + + +@@ -183,6 +188,47 @@ public class PropertyUtilsBean { + } + } + ++ /** ++ * Resets the {@link BeanIntrospector} objects registered at this instance. After this ++ * method was called, only the default {@code BeanIntrospector} is registered. ++ * ++ * @since 1.9 ++ */ ++ public final void resetBeanIntrospectors() { ++ introspectors.clear(); ++ introspectors.add(DefaultBeanIntrospector.INSTANCE); ++ introspectors.add(SuppressPropertiesBeanIntrospector.SUPPRESS_CLASS); ++ } ++ ++ /** ++ * Adds a BeanIntrospector. This object is invoked when the ++ * property descriptors of a class need to be obtained. ++ * ++ * @param introspector the BeanIntrospector to be added (must ++ * not be null ++ * @throws IllegalArgumentException if the argument is null ++ * @since 1.9 ++ */ ++ public void addBeanIntrospector(final BeanIntrospector introspector) { ++ if (introspector == null) { ++ throw new IllegalArgumentException( ++ "BeanIntrospector must not be null!"); ++ } ++ introspectors.add(introspector); ++ } ++ ++ /** ++ * Removes the specified BeanIntrospector. ++ * ++ * @param introspector the BeanIntrospector to be removed ++ * @return true if the BeanIntrospector existed and ++ * could be removed, false otherwise ++ * @since 1.9 ++ */ ++ public boolean removeBeanIntrospector(final BeanIntrospector introspector) { ++ return introspectors.remove(introspector); ++ } ++ + /** + * Clear any cached property descriptors information for all classes + * loaded by any class loaders. This is useful in cases where class +@@ -909,17 +955,12 @@ public class PropertyUtilsBean { + return (null); + } + +- PropertyDescriptor[] descriptors = getPropertyDescriptors(bean); +- if (descriptors != null) { +- +- for (int i = 0; i < descriptors.length; i++) { +- if (name.equals(descriptors[i].getName())) { +- return (descriptors[i]); +- } +- } ++ final BeanIntrospectionData data = getIntrospectionData(bean.getClass()); ++ PropertyDescriptor result = data.getDescriptor(name); ++ if (result != null) { ++ return result; + } + +- PropertyDescriptor result = null; + FastHashMap mappedDescriptors = + getMappedPropertyDescriptors(bean); + if (mappedDescriptors == null) { +@@ -960,110 +1001,10 @@ public class PropertyUtilsBean { + * @exception IllegalArgumentException if beanClass is null + */ + public PropertyDescriptor[] +- getPropertyDescriptors(Class beanClass) { +- +- if (beanClass == null) { +- throw new IllegalArgumentException("No bean class specified"); +- } +- +- // Look up any cached descriptors for this bean class +- PropertyDescriptor[] descriptors = null; +- descriptors = +- (PropertyDescriptor[]) descriptorsCache.get(beanClass); +- if (descriptors != null) { +- return (descriptors); +- } +- +- // Introspect the bean and cache the generated descriptors +- BeanInfo beanInfo = null; +- try { +- beanInfo = Introspector.getBeanInfo(beanClass); +- } catch (IntrospectionException e) { +- return (new PropertyDescriptor[0]); +- } +- descriptors = beanInfo.getPropertyDescriptors(); +- if (descriptors == null) { +- descriptors = new PropertyDescriptor[0]; +- } +- +- // ----------------- Workaround for Bug 28358 --------- START ------------------ +- // +- // The following code fixes an issue where IndexedPropertyDescriptor behaves +- // Differently in different versions of the JDK for 'indexed' properties which +- // use java.util.List (rather than an array). +- // +- // If you have a Bean with the following getters/setters for an indexed property: +- // +- // public List getFoo() +- // public Object getFoo(int index) +- // public void setFoo(List foo) +- // public void setFoo(int index, Object foo) +- // +- // then the IndexedPropertyDescriptor's getReadMethod() and getWriteMethod() +- // behave as follows: +- // +- // JDK 1.3.1_04: returns valid Method objects from these methods. +- // JDK 1.4.2_05: returns null from these methods. +- // +- for (int i = 0; i < descriptors.length; i++) { +- if (descriptors[i] instanceof IndexedPropertyDescriptor) { +- IndexedPropertyDescriptor descriptor = (IndexedPropertyDescriptor)descriptors[i]; +- String propName = descriptor.getName().substring(0, 1).toUpperCase() + +- descriptor.getName().substring(1); +- +- if (descriptor.getReadMethod() == null) { +- String methodName = descriptor.getIndexedReadMethod() != null +- ? descriptor.getIndexedReadMethod().getName() +- : "get" + propName; +- Method readMethod = MethodUtils.getMatchingAccessibleMethod(beanClass, +- methodName, +- EMPTY_CLASS_PARAMETERS); +- if (readMethod != null) { +- try { +- descriptor.setReadMethod(readMethod); +- } catch(Exception e) { +- log.error("Error setting indexed property read method", e); +- } +- } +- } +- if (descriptor.getWriteMethod() == null) { +- String methodName = descriptor.getIndexedWriteMethod() != null +- ? descriptor.getIndexedWriteMethod().getName() +- : "set" + propName; +- Method writeMethod = MethodUtils.getMatchingAccessibleMethod(beanClass, +- methodName, +- LIST_CLASS_PARAMETER); +- if (writeMethod == null) { +- Method[] methods = beanClass.getMethods(); +- for (int j = 0; j < methods.length; j++) { +- if (methods[j].getName().equals(methodName)) { +- Class[] parameterTypes = methods[j].getParameterTypes(); +- if (parameterTypes.length == 1 && +- List.class.isAssignableFrom(parameterTypes[0])) { +- writeMethod = methods[j]; +- break; +- } +- } +- } +- } +- if (writeMethod != null) { +- try { +- descriptor.setWriteMethod(writeMethod); +- } catch(Exception e) { +- log.error("Error setting indexed property write method", e); +- } +- } +- } +- } +- } +- // ----------------- Workaround for Bug 28358 ---------- END ------------------- +- +- descriptorsCache.put(beanClass, descriptors); +- return (descriptors); +- ++ getPropertyDescriptors(Class beanClass) { ++ return getIntrospectionData(beanClass).getDescriptors(); + } + +- + /** + *

Retrieve the property descriptors for the specified bean, + * introspecting and caching them the first time a particular bean class +@@ -2248,4 +2189,52 @@ public class PropertyUtilsBean { + + } + } ++ ++ /** ++ * Obtains the {@code BeanIntrospectionData} object describing the specified bean ++ * class. This object is looked up in the internal cache. If necessary, introspection ++ * is performed now on the affected bean class, and the results object is created. ++ * ++ * @param beanClass the bean class in question ++ * @return the {@code BeanIntrospectionData} object for this class ++ * @throws IllegalArgumentException if the bean class is null ++ */ ++ private BeanIntrospectionData getIntrospectionData(final Class beanClass) { ++ if (beanClass == null) { ++ throw new IllegalArgumentException("No bean class specified"); ++ } ++ ++ // Look up any cached information for this bean class ++ BeanIntrospectionData data = (BeanIntrospectionData) descriptorsCache.get(beanClass); ++ if (data == null) { ++ data = fetchIntrospectionData(beanClass); ++ descriptorsCache.put(beanClass, data); ++ } ++ ++ return data; ++ } ++ ++ /** ++ * Performs introspection on the specified class. This method invokes all {@code BeanIntrospector} objects that were ++ * added to this instance. ++ * ++ * @param beanClass the class to be inspected ++ * @return a data object with the results of introspection ++ */ ++ private BeanIntrospectionData fetchIntrospectionData(final Class beanClass) { ++ final DefaultIntrospectionContext ictx = new DefaultIntrospectionContext(beanClass); ++ ++ Iterator it = introspectors.iterator(); ++ while (it.hasNext()) { ++ final BeanIntrospector bi = (BeanIntrospector) it.next(); ++ ++ try { ++ bi.introspect(ictx); ++ } catch (final IntrospectionException iex) { ++ log.error("Exception during introspection", iex); ++ } ++ } ++ ++ return new BeanIntrospectionData(ictx.getPropertyDescriptors()); ++ } + } +diff --git a/src/main/java/org/apache/commons/beanutils/SuppressPropertiesBeanIntrospector.java b/src/main/java/org/apache/commons/beanutils/SuppressPropertiesBeanIntrospector.java +new file mode 100644 +index 0000000..9d4e3b3 +--- /dev/null ++++ b/src/main/java/org/apache/commons/beanutils/SuppressPropertiesBeanIntrospector.java +@@ -0,0 +1,75 @@ ++package org.apache.commons.beanutils; ++ ++import java.beans.IntrospectionException; ++import java.util.Collection; ++import java.util.Collections; ++import java.util.HashSet; ++import java.util.Set; ++ ++/** ++ *

++ * A specialized {@code BeanIntrospector} implementation which suppresses some properties. ++ *

++ *

++ * An instance of this class is passed a set with the names of the properties it should ++ * process. During introspection of a bean class it removes all these properties from the ++ * {@link IntrospectionContext}. So effectively, properties added by a different ++ * {@code BeanIntrospector} are removed again. ++ *

++ * ++ * @version $Id$ ++ * @since 1.9.2 ++ */ ++public class SuppressPropertiesBeanIntrospector implements BeanIntrospector { ++ /** ++ * A specialized instance which is configured to suppress the special {@code class} ++ * properties of Java beans. Unintended access to the property {@code class} (which is ++ * common to all Java objects) can be a security risk because it also allows access to ++ * the class loader. Adding this instance as {@code BeanIntrospector} to an instance ++ * of {@code PropertyUtilsBean} suppresses the {@code class} property; it can then no ++ * longer be accessed. ++ */ ++ public static final SuppressPropertiesBeanIntrospector SUPPRESS_CLASS = ++ new SuppressPropertiesBeanIntrospector(Collections.singleton("class")); ++ ++ /** A set with the names of the properties to be suppressed. */ ++ private final Set propertyNames; ++ ++ /** ++ * Creates a new instance of {@code SuppressPropertiesBeanIntrospector} and sets the ++ * names of the properties to be suppressed. ++ * ++ * @param propertiesToSuppress the names of the properties to be suppressed (must not ++ * be null) ++ * @throws IllegalArgumentException if the collection with property names is ++ * null ++ */ ++ public SuppressPropertiesBeanIntrospector(Collection propertiesToSuppress) { ++ if (propertiesToSuppress == null) { ++ throw new IllegalArgumentException("Property names must not be null!"); ++ } ++ ++ propertyNames = Collections.unmodifiableSet(new HashSet( ++ propertiesToSuppress)); ++ } ++ ++ /** ++ * Returns a (unmodifiable) set with the names of the properties which are suppressed ++ * by this {@code BeanIntrospector}. ++ * ++ * @return a set with the names of the suppressed properties ++ */ ++ public Set getSuppressedProperties() { ++ return propertyNames; ++ } ++ ++ /** ++ * {@inheritDoc} This implementation removes all properties from the given context it ++ * is configured for. ++ */ ++ public void introspect(IntrospectionContext icontext) throws IntrospectionException { ++ for (String property : getSuppressedProperties()) { ++ icontext.removePropertyDescriptor(property); ++ } ++ } ++} +diff --git a/src/test/java/org/apache/commons/beanutils/SuppressPropertiesBeanIntrospectorTestCase.java b/src/test/java/org/apache/commons/beanutils/SuppressPropertiesBeanIntrospectorTestCase.java +new file mode 100644 +index 0000000..ed93e70 +--- /dev/null ++++ b/src/test/java/org/apache/commons/beanutils/SuppressPropertiesBeanIntrospectorTestCase.java +@@ -0,0 +1,127 @@ ++package org.apache.commons.beanutils; ++ ++import java.beans.IntrospectionException; ++import java.beans.PropertyDescriptor; ++import java.util.Arrays; ++import java.util.Collection; ++import java.util.HashSet; ++import java.util.Set; ++ ++import junit.framework.TestCase; ++ ++/** ++ * Test class for {@code SuppressPropertiesBeanIntrospector}. ++ * ++ * @version $Id$ ++ */ ++public class SuppressPropertiesBeanIntrospectorTestCase extends TestCase { ++ /** ++ * Tries to create an instance without properties. ++ */ ++ public void testInitNoPropertyNames() { ++ try { ++ new SuppressPropertiesBeanIntrospector(null); ++ fail("Missing properties not detected!"); ++ } catch (IllegalArgumentException iaex) { ++ // ok ++ } ++ } ++ ++ /** ++ * Tests whether the expected properties have been removed during introspection. ++ */ ++ public void testRemovePropertiesDuringIntrospection() throws IntrospectionException { ++ String[] properties = { "test", "other", "oneMore" }; ++ SuppressPropertiesBeanIntrospector introspector = new SuppressPropertiesBeanIntrospector( ++ Arrays.asList(properties)); ++ IntrospectionContextTestImpl context = new IntrospectionContextTestImpl(); ++ ++ introspector.introspect(context); ++ assertEquals("Wrong number of removed properties", properties.length, context ++ .getRemovedProperties().size()); ++ for (String property : properties) { ++ assertTrue("Property not removed: " + property, context ++ .getRemovedProperties().contains(property)); ++ } ++ } ++ ++ /** ++ * Tests that a defensive copy is created from the collection with properties to be ++ * removed. ++ */ ++ public void testPropertyNamesDefensiveCopy() throws IntrospectionException { ++ Collection properties = new HashSet(); ++ properties.add("prop1"); ++ SuppressPropertiesBeanIntrospector introspector = new SuppressPropertiesBeanIntrospector( ++ properties); ++ properties.add("prop2"); ++ IntrospectionContextTestImpl context = new IntrospectionContextTestImpl(); ++ ++ introspector.introspect(context); ++ assertEquals("Wrong number of removed properties", 1, context ++ .getRemovedProperties().size()); ++ assertTrue("Wrong removed property", ++ context.getRemovedProperties().contains("prop1")); ++ } ++ ++ /** ++ * Tests that the set with properties to be removed cannot be modified. ++ */ ++ public void testGetSuppressedPropertiesModify() { ++ SuppressPropertiesBeanIntrospector introspector = new SuppressPropertiesBeanIntrospector( ++ Arrays.asList("p1", "p2")); ++ Set properties = introspector.getSuppressedProperties(); ++ try { ++ properties.add("anotherProperty"); ++ fail("Could modify properties"); ++ } catch (UnsupportedOperationException uoex) { ++ // ok ++ } ++ } ++ ++ /** ++ * A test implementation of IntrospectionContext which collects the properties which ++ * have been removed. ++ */ ++ private static class IntrospectionContextTestImpl implements IntrospectionContext { ++ /** Stores the names of properties which have been removed. */ ++ private final Set removedProperties = new HashSet(); ++ ++ /** ++ * Returns the names of properties which have been removed. ++ * ++ * @return the set with removed properties ++ */ ++ public Set getRemovedProperties() { ++ return removedProperties; ++ } ++ ++ public Class getTargetClass() { ++ throw new UnsupportedOperationException("Unexpected method call!"); ++ } ++ ++ public void addPropertyDescriptor(PropertyDescriptor desc) { ++ throw new UnsupportedOperationException("Unexpected method call!"); ++ } ++ ++ public void addPropertyDescriptors(PropertyDescriptor[] descriptors) { ++ throw new UnsupportedOperationException("Unexpected method call!"); ++ } ++ ++ public boolean hasProperty(String name) { ++ throw new UnsupportedOperationException("Unexpected method call!"); ++ } ++ ++ public PropertyDescriptor getPropertyDescriptor(String name) { ++ throw new UnsupportedOperationException("Unexpected method call!"); ++ } ++ ++ public void removePropertyDescriptor(String name) { ++ removedProperties.add(name); ++ } ++ ++ public Set propertyNames() { ++ throw new UnsupportedOperationException("Unexpected method call!"); ++ } ++ } ++} +diff --git a/src/test/java/org/apache/commons/beanutils/bugs/Jira463TestCase.java b/src/test/java/org/apache/commons/beanutils/bugs/Jira463TestCase.java +new file mode 100644 +index 0000000..31ac31c +--- /dev/null ++++ b/src/test/java/org/apache/commons/beanutils/bugs/Jira463TestCase.java +@@ -0,0 +1,48 @@ ++/* ++ * 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.commons.beanutils.bugs; ++ ++import junit.framework.TestCase; ++ ++import org.apache.commons.beanutils.AlphaBean; ++import org.apache.commons.beanutils.BeanUtilsBean; ++import org.apache.commons.beanutils.SuppressPropertiesBeanIntrospector; ++ ++/** ++ * Class loader vulnerability in DefaultResolver ++ * ++ * @version $Id$ ++ * @see https://issues.apache.org/jira/browse/BEANUTILS-463 ++ */ ++public class Jira463TestCase extends TestCase { ++ /** ++ * Tests that with a specialized {@code BeanIntrospector} implementation the class ++ * property can be suppressed. ++ */ ++ public void testSuppressClassProperty() throws Exception { ++ BeanUtilsBean bub = new BeanUtilsBean(); ++ bub.getPropertyUtils().addBeanIntrospector( ++ SuppressPropertiesBeanIntrospector.SUPPRESS_CLASS); ++ AlphaBean bean = new AlphaBean(); ++ try { ++ bub.getProperty(bean, "class"); ++ fail("Could access class property!"); ++ } catch (NoSuchMethodException ex) { ++ // ok ++ } ++ } ++} +diff --git a/src/test/java/org/apache/commons/beanutils/bugs/Jira520TestCase.java b/src/test/java/org/apache/commons/beanutils/bugs/Jira520TestCase.java +new file mode 100644 +index 0000000..2a70811 +--- /dev/null ++++ b/src/test/java/org/apache/commons/beanutils/bugs/Jira520TestCase.java +@@ -0,0 +1,39 @@ ++package org.apache.commons.beanutils.bugs; ++ ++import org.apache.commons.beanutils.AlphaBean; ++import org.apache.commons.beanutils.BeanUtilsBean; ++import org.apache.commons.beanutils.SuppressPropertiesBeanIntrospector; ++ ++import junit.framework.TestCase; ++ ++/** ++ * Fix CVE: https://nvd.nist.gov/vuln/detail/CVE-2014-0114 ++ * ++ * @see https://issues.apache.org/jira/browse/BEANUTILS-520 ++ */ ++public class Jira520TestCase extends TestCase { ++ /** ++ * By default opt-in to security that does not allow access to "class". ++ */ ++ public void testSuppressClassPropertyByDefault() throws Exception { ++ final BeanUtilsBean bub = new BeanUtilsBean(); ++ final AlphaBean bean = new AlphaBean(); ++ try { ++ bub.getProperty(bean, "class"); ++ fail("Could access class property!"); ++ } catch (final NoSuchMethodException ex) { ++ // ok ++ } ++ } ++ ++ /** ++ * Allow opt-out to make your app less secure but allow access to "class". ++ */ ++ public void testAllowAccessToClassProperty() throws Exception { ++ final BeanUtilsBean bub = new BeanUtilsBean(); ++ bub.getPropertyUtils().removeBeanIntrospector(SuppressPropertiesBeanIntrospector.SUPPRESS_CLASS); ++ final AlphaBean bean = new AlphaBean(); ++ String result = bub.getProperty(bean, "class"); ++ assertEquals("Class property should have been accessed", "class org.apache.commons.beanutils.AlphaBean", result); ++ } ++} +\ No newline at end of file +-- +2.21.0 + diff --git a/SPECS/apache-commons-beanutils.spec b/SPECS/apache-commons-beanutils.spec index 877f8a4..1c1ddd7 100644 --- a/SPECS/apache-commons-beanutils.spec +++ b/SPECS/apache-commons-beanutils.spec @@ -3,13 +3,15 @@ Name: apache-%{short_name} Version: 1.8.3 -Release: 14%{?dist} +Release: 15%{?dist} Summary: Java utility methods for accessing and modifying the properties of arbitrary JavaBeans License: ASL 2.0 URL: http://commons.apache.org/%{base_name} BuildArch: noarch Source0: http://archive.apache.org/dist/commons/%{base_name}/source/%{short_name}-%{version}-src.tar.gz +Patch0: CVE-2014-0114.patch + BuildRequires: maven-local BuildRequires: mvn(commons-collections:commons-collections) BuildRequires: mvn(commons-logging:commons-logging) @@ -36,6 +38,9 @@ Obsoletes: jakarta-%{short_name}-javadoc <= 0:1.7.0 %prep %setup -q -n %{short_name}-%{version}-src sed -i 's/\r//' *.txt +find -name '*.java' -exec sed -i 's/\r//' {} + + +%patch0 -p1 %pom_remove_plugin :maven-antrun-plugin %pom_remove_plugin :maven-assembly-plugin @@ -60,6 +65,10 @@ sed -i 's/\r//' *.txt %doc LICENSE.txt NOTICE.txt %changelog +* Fri Dec 20 2019 Marian Koncek - 1.8.3-15 +- Fix CVE-2014-0114 +- Fix CVE-2019-10086 + * Fri Dec 27 2013 Daniel Mach - 1.8.3-14 - Mass rebuild 2013-12-27