This is an automated email from the ASF dual-hosted git repository.
ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-beanutils.git
The following commit(s) were added to refs/heads/master by this push:
new bd20740d Add
org.apache.commons.beanutils.SuppressPropertiesBeanIntrospector.SUPPRESS_DECLARING_CLASS
bd20740d is described below
commit bd20740da25b69552ddef8523beec0837297eaf9
Author: Gary Gregory <[email protected]>
AuthorDate: Sun May 25 09:07:45 2025 -0400
Add
org.apache.commons.beanutils.SuppressPropertiesBeanIntrospector.SUPPRESS_DECLARING_CLASS
---
src/changes/changes.xml | 16 +++
.../commons/beanutils2/PropertyUtilsBean.java | 1 +
.../SuppressPropertiesBeanIntrospector.java | 14 ++-
.../org/apache/commons/beanutils2/TestEnum.java | 33 +++++++
.../apache/commons/beanutils2/package-info.java | 8 +-
.../beanutils2/bugs/EnumDeclaringClassTest.java | 108 +++++++++++++++++++++
6 files changed, 178 insertions(+), 2 deletions(-)
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 4f82d3cc..e0ee838b 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -54,6 +54,7 @@
<action type="fix" dev="ggregory" due-to="Gary
Gregory">ResultSetIterator.set(String, Object) now throws
IllegalArgumentException instead of RuntimeException to wrap cases of
SQLException.</action>
<action type="fix" dev="ggregory" due-to="Gary
Gregory">ResultSetIterator.set(String, String, Object) now throws
IllegalArgumentException instead of RuntimeException to wrap cases of
SQLException.</action>
<!-- ADD -->
+ <action type="add" dev="ggregory" due-to="Gary Gregory">Add
org.apache.commons.beanutils.SuppressPropertiesBeanIntrospector.SUPPRESS_DECLARING_CLASS.</action>
<!-- UPDATE -->
<action type="update" dev="ggregory" due-to="Gary Gregory,
Dependabot">Bump org.apache.commons:commons-parent from 78 to 84 #348.</action>
<action type="update" dev="ggregory" due-to="Gary Gregory">Bump
commons-logging:commons-logging from 1.3.4 to 1.3.5.</action>
@@ -258,6 +259,21 @@
Do not implement Serializable.
</action>
</release>
+ <release version="1.11.0" date="YYYY-MM-DD" description="This is a
maintenance release and requires Java 8.">
+ <!-- FIX -->
+ <action type="fix" dev="ggregory" due-to="Gary
Gregory">BeanComparator.compare(T, T) now throws IllegalArgumentException
instead of RuntimeException to wrap all cases of
ReflectiveOperationException.</action>
+ <action type="fix" dev="ggregory" due-to="Gary
Gregory">MappedMethodReference.get() now throws IllegalStateException instead
of RuntimeException to wrap cases of NoSuchMethodException.</action>
+ <action type="fix" dev="ggregory" due-to="Gary
Gregory">ResultSetIterator.get(String) now throws IllegalArgumentException
instead of RuntimeException to wrap cases of SQLException.</action>
+ <action type="fix" dev="ggregory" due-to="Gary
Gregory">ResultSetIterator.hasNext() now throws IllegalStateException instead
of RuntimeException to wrap cases of SQLException.</action>
+ <action type="fix" dev="ggregory" due-to="Gary
Gregory">ResultSetIterator.next() now throws IllegalStateException instead of
RuntimeException to wrap cases of SQLException.</action>
+ <action type="fix" dev="ggregory" due-to="Gary
Gregory">ResultSetIterator.set(String, Object) now throws
IllegalArgumentException instead of RuntimeException to wrap cases of
SQLException.</action>
+ <action type="fix" dev="ggregory" due-to="Gary
Gregory">ResultSetIterator.set(String, String, Object) now throws
IllegalArgumentException instead of RuntimeException to wrap cases of
SQLException.</action>
+ <!-- ADD -->
+ <action type="add" dev="ggregory" due-to="Gary Gregory">Add
org.apache.commons.beanutils.SuppressPropertiesBeanIntrospector.SUPPRESS_DECLARING_CLASS.</action>
+ <!-- UPDATE -->
+ <action dev="ggregory" type="update" due-to="Gary Gregory">Bump
org.apache.commons:commons-parent from 81 to 84.</action>
+ <action dev="ggregory" type="update" due-to="Gary Gregory">Bump
commons-logging:commons-logging from 1.3.4 to 1.3.5.</action>
+ </release>
<release version="1.10.1" date="2025-01-31" description="This is a
maintenance release and requires Java 8.">
<!-- FIX -->
<action type="fix" issue="BEANUTILS-541" dev="ggregory" due-to="Sergey
Chernov">FluentPropertyBeanIntrospector concurrency issue (backport to 1.X)
#325.</action>
diff --git a/src/main/java/org/apache/commons/beanutils2/PropertyUtilsBean.java
b/src/main/java/org/apache/commons/beanutils2/PropertyUtilsBean.java
index d1fe015b..f16f06e3 100644
--- a/src/main/java/org/apache/commons/beanutils2/PropertyUtilsBean.java
+++ b/src/main/java/org/apache/commons/beanutils2/PropertyUtilsBean.java
@@ -1201,6 +1201,7 @@ public class PropertyUtilsBean {
introspectors.clear();
introspectors.add(DefaultBeanIntrospector.INSTANCE);
introspectors.add(SuppressPropertiesBeanIntrospector.SUPPRESS_CLASS);
+
introspectors.add(SuppressPropertiesBeanIntrospector.SUPPRESS_DECLARING_CLASS);
}
/**
diff --git
a/src/main/java/org/apache/commons/beanutils2/SuppressPropertiesBeanIntrospector.java
b/src/main/java/org/apache/commons/beanutils2/SuppressPropertiesBeanIntrospector.java
index bb297ffd..8d9b3562 100644
---
a/src/main/java/org/apache/commons/beanutils2/SuppressPropertiesBeanIntrospector.java
+++
b/src/main/java/org/apache/commons/beanutils2/SuppressPropertiesBeanIntrospector.java
@@ -35,6 +35,7 @@ import java.util.Set;
* @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
@@ -42,7 +43,18 @@ public class SuppressPropertiesBeanIntrospector implements
BeanIntrospector {
*/
public static final SuppressPropertiesBeanIntrospector SUPPRESS_CLASS =
new SuppressPropertiesBeanIntrospector(Collections.singleton("class"));
- /** A set with the names of the properties to be suppressed. */
+ /**
+ * A specialized instance which is configured to suppress the special
{@code class} properties of Java beans. Unintended access to the call for
+ * {@code declaringClass} (which is common to all Java {@code enum}) 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.
+ *
+ * @since 2.0.0-M2
+ */
+ public static final SuppressPropertiesBeanIntrospector
SUPPRESS_DECLARING_CLASS = new SuppressPropertiesBeanIntrospector(
+ Collections.singleton("declaringClass"));
+
+/** A set with the names of the properties to be suppressed. */
private final Set<String> propertyNames;
/**
diff --git a/src/main/java/org/apache/commons/beanutils2/TestEnum.java
b/src/main/java/org/apache/commons/beanutils2/TestEnum.java
new file mode 100644
index 00000000..a64142a0
--- /dev/null
+++ b/src/main/java/org/apache/commons/beanutils2/TestEnum.java
@@ -0,0 +1,33 @@
+/*
+ * 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
+ *
+ * https://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.beanutils2;
+
+/**
+ * An {@code enum} test fixture.
+ */
+public enum TestEnum {
+
+ /** Test fixture. */
+ A,
+
+ /** Test fixture. */
+ B,
+
+ /** Test fixture. */
+ C
+}
diff --git a/src/main/java/org/apache/commons/beanutils2/package-info.java
b/src/main/java/org/apache/commons/beanutils2/package-info.java
index a2bd66f2..50b8a996 100644
--- a/src/main/java/org/apache/commons/beanutils2/package-info.java
+++ b/src/main/java/org/apache/commons/beanutils2/package-info.java
@@ -424,7 +424,7 @@
*
* <p>A good use case for suppressing properties is the special {@code class}
* property which is per default available for all beans; it is generated from
the
- * {@code getClass()</code> method inherited from <code>Object} which follows
the
+ * {@code getClass()} method inherited from {@code Object} which follows the
* naming conventions for property get methods. Exposing this property in an
* uncontrolled way can lead to a security vulnerability as it allows access to
* the class loader. More information can be found at
@@ -437,6 +437,12 @@
* {@code SUPPRESS_CLASS} constant of
* {@code SuppressPropertiesBeanIntrospector}.</p>
*
+ * <p>Another problematic property is the {@code enum} "declaredClass"
property,
+ * through which you can also access that class' class loader. The {@code
SuppressPropertiesBeanIntrospector}
+ * provides {@code SUPPRESS_DECLARING_CLASS} to workaround this issue.</p>
+ *
+ * <p>Both {@code SUPPRESS_CLASS} and {@code SUPPRESS_DECLARING_CLASS} are
enabled by default.</p>
+ *
* <a id="dynamic"></a>
* <h2>3. Dynamic Beans (DynaBeans)</h2>
*
diff --git
a/src/test/java/org/apache/commons/beanutils2/bugs/EnumDeclaringClassTest.java
b/src/test/java/org/apache/commons/beanutils2/bugs/EnumDeclaringClassTest.java
new file mode 100644
index 00000000..e0d1c229
--- /dev/null
+++
b/src/test/java/org/apache/commons/beanutils2/bugs/EnumDeclaringClassTest.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
+ *
+ * https://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.beanutils2.bugs;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import org.apache.commons.beanutils2.BeanUtilsBean;
+import org.apache.commons.beanutils2.PropertyUtilsBean;
+import org.apache.commons.beanutils2.SuppressPropertiesBeanIntrospector;
+import org.apache.commons.beanutils2.TestEnum;
+import org.junit.jupiter.api.Test;
+
+public class EnumDeclaringClassTest {
+
+ public static class Fixture {
+
+ String name = "default";
+ TestEnum testEnum = TestEnum.A;
+
+ public String getName() {
+ return name;
+ }
+
+ public TestEnum getTestEnum() {
+ return testEnum;
+ }
+
+ public void setName(final String name) {
+ this.name = name;
+ }
+
+ public void setTestEnum(final TestEnum day) {
+ this.testEnum = day;
+ }
+ }
+
+ /**
+ * Allow opt-out to make your app less secure but allow access to
"declaringClass".
+ */
+ @Test
+ public void testAllowAccessToClassPropertyFromBeanUtilsBean() throws
ReflectiveOperationException {
+ final BeanUtilsBean bub = new BeanUtilsBean();
+ final PropertyUtilsBean propertyUtilsBean = bub.getPropertyUtils();
+
propertyUtilsBean.removeBeanIntrospector(SuppressPropertiesBeanIntrospector.SUPPRESS_DECLARING_CLASS);
+ final Fixture fixture = new Fixture();
+ final String string = bub.getProperty(fixture,
"testEnum.declaringClass");
+ assertEquals(TestEnum.class.getName(), string);
+ final Class<TestEnum> teClass = assertInstanceOf(Class.class,
propertyUtilsBean.getNestedProperty(fixture, "testEnum.declaringClass"));
+ final ClassLoader classLoader = teClass.getClassLoader();
+ assertNotNull(classLoader);
+ assertNotNull(bub.getProperty(fixture,
"testEnum.declaringClass.classLoader"));
+ assertInstanceOf(ClassLoader.class,
propertyUtilsBean.getNestedProperty(fixture,
"testEnum.declaringClass.classLoader"));
+ }
+
+ /**
+ * Allow opt-out to make your app less secure but allow access to
"declaringClass".
+ */
+ @Test
+ public void testAllowAccessToClassPropertyFromPropertyUtilsBean() throws
ReflectiveOperationException {
+ final PropertyUtilsBean propertyUtilsBean = new PropertyUtilsBean();
+
propertyUtilsBean.removeBeanIntrospector(SuppressPropertiesBeanIntrospector.SUPPRESS_DECLARING_CLASS);
+ final Fixture fixture = new Fixture();
+ final Object cls = propertyUtilsBean.getNestedProperty(fixture,
"testEnum.declaringClass");
+ final Class<TestEnum> teClass = assertInstanceOf(Class.class, cls);
+ final ClassLoader classLoader = teClass.getClassLoader();
+ assertNotNull(classLoader);
+ assertInstanceOf(ClassLoader.class,
propertyUtilsBean.getNestedProperty(fixture,
"testEnum.declaringClass.classLoader"));
+ }
+
+ /**
+ * By default opt-in to security that does not allow access to
"declaringClass".
+ */
+ @Test
+ public void testSuppressClassPropertyByDefaultFromBeanUtilsBean() throws
ReflectiveOperationException {
+ final Fixture fixture = new Fixture();
+ final BeanUtilsBean bub = new BeanUtilsBean();
+ assertThrows(NoSuchMethodException.class, () ->
bub.getProperty(fixture, "testEnum.declaringClass.classLoader"));
+ assertThrows(NoSuchMethodException.class, () ->
bub.getPropertyUtils().getNestedProperty(fixture,
"testEnum.declaringClass.classLoader"));
+ }
+
+ /**
+ * By default opt-in to security that does not allow access to
"declaringClass".
+ */
+ @Test
+ public void testSuppressClassPropertyByDefaultFromPropertyUtilsBean()
throws ReflectiveOperationException {
+ final Fixture fixture = new Fixture();
+ final PropertyUtilsBean propertyUtilsBean = new PropertyUtilsBean();
+ assertThrows(NoSuchMethodException.class, () ->
propertyUtilsBean.getNestedProperty(fixture,
"testEnum.declaringClass.classLoader"));
+ }
+}