This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch 1.X
in repository https://gitbox.apache.org/repos/asf/commons-beanutils.git


The following commit(s) were added to refs/heads/1.X by this push:
     new 28ad955a Add 
org.apache.commons.beanutils.SuppressPropertiesBeanIntrospector.SUPPRESS_DECLARING_CLASS
28ad955a is described below

commit 28ad955a1613ed5885870cc7da52093c1ce739dc
Author: Gary Gregory <garydgreg...@gmail.com>
AuthorDate: Sun May 25 09:07:32 2025 -0400

    Add 
org.apache.commons.beanutils.SuppressPropertiesBeanIntrospector.SUPPRESS_DECLARING_CLASS
---
 pom.xml                                            |  11 ++-
 src/changes/changes.xml                            |   3 +-
 .../commons/beanutils/PropertyUtilsBean.java       |   1 +
 .../SuppressPropertiesBeanIntrospector.java        |  24 +++--
 .../org/apache/commons/beanutils/package-info.java |  18 ++--
 .../org/apache/commons/beanutils/TestEnum.java     |  33 +++++++
 .../beanutils/bugs/EnumDeclaringClassTest.java     | 108 +++++++++++++++++++++
 7 files changed, 180 insertions(+), 18 deletions(-)

diff --git a/pom.xml b/pom.xml
index f803e098..bf7133e8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -24,7 +24,7 @@
   <modelVersion>4.0.0</modelVersion>
   <groupId>commons-beanutils</groupId>
   <artifactId>commons-beanutils</artifactId>
-  <version>1.10.2-SNAPSHOT</version>
+  <version>1.11.0-SNAPSHOT</version>
   <name>Apache Commons BeanUtils</name>
 
   <inceptionYear>2000</inceptionYear>
@@ -38,8 +38,8 @@
       <commons.componentid>beanutils</commons.componentid>
       <commons.main.branch>1.X</commons.main.branch>
       <commons.release.branch>release-1.x</commons.release.branch>
-      <commons.release.version>1.10.1</commons.release.version>
-      <commons.release.next>1.10.2</commons.release.next>
+      <commons.release.version>1.11.0</commons.release.version>
+      <commons.release.next>1.11.1</commons.release.next>
       <commons.jira.id>BEANUTILS</commons.jira.id>
       <commons.jira.pid>12310460</commons.jira.pid>
       <!-- Limit memory size see BEANUTILS-291; allow command-line override -->
@@ -99,6 +99,11 @@
       <artifactId>junit-vintage-engine</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.junit.jupiter</groupId>
+      <artifactId>junit-jupiter</artifactId>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
   <build>
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index fe9cdfb1..c652852e 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -28,7 +28,7 @@
     <title>Release Notes</title>
   </properties>
   <body>
-    <release version="1.10.2" date="YYYY-MM-DD" description="This is a 
maintenance release and requires Java 8.">
+    <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>
@@ -38,6 +38,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 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>
diff --git a/src/main/java/org/apache/commons/beanutils/PropertyUtilsBean.java 
b/src/main/java/org/apache/commons/beanutils/PropertyUtilsBean.java
index 7955f850..f7b1509b 100644
--- a/src/main/java/org/apache/commons/beanutils/PropertyUtilsBean.java
+++ b/src/main/java/org/apache/commons/beanutils/PropertyUtilsBean.java
@@ -1478,6 +1478,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/beanutils/SuppressPropertiesBeanIntrospector.java
 
b/src/main/java/org/apache/commons/beanutils/SuppressPropertiesBeanIntrospector.java
index c5fa1736..61b45f28 100644
--- 
a/src/main/java/org/apache/commons/beanutils/SuppressPropertiesBeanIntrospector.java
+++ 
b/src/main/java/org/apache/commons/beanutils/SuppressPropertiesBeanIntrospector.java
@@ -36,16 +36,24 @@ 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 {@code BeanIntrospector} to 
an instance
-     * of {@code PropertyUtilsBean} suppresses the {@code class} property; it 
can then no
-     * longer be accessed.
+     * 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 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 1.11.0
      */
-    public static final SuppressPropertiesBeanIntrospector SUPPRESS_CLASS =
-            new 
SuppressPropertiesBeanIntrospector(Collections.singleton("class"));
+    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/beanutils/package-info.java 
b/src/main/java/org/apache/commons/beanutils/package-info.java
index 0f7e7740..252814cc 100644
--- a/src/main/java/org/apache/commons/beanutils/package-info.java
+++ b/src/main/java/org/apache/commons/beanutils/package-info.java
@@ -420,20 +420,26 @@
  * then be removed if they have been detected by other 
<code>BeanIntrospector</code>
  * instances during processing of a bean class.</p>
  *
- * <p>A good use case for suppressing properties is the special 
<code>class</code>
+ * <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</code> 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
  * <a href="https://issues.apache.org/jira/browse/BEANUTILS-463";>
  * https://issues.apache.org/jira/browse/BEANUTILS-463</a>.</p>
  *
- * <p>Because the <code>class</code> property is undesired in many use cases
- * there is already an instance of 
<code>SuppressPropertiesBeanIntrospector</code>
+ * <p>Because the {@code class} property is undesired in many use cases
+ * there is already an instance of {@code SuppressPropertiesBeanIntrospector}
  * which is configured to suppress this property. It can be obtained via the
- * <code>SUPPRESS_CLASS</code> constant of
- * <code>SuppressPropertiesBeanIntrospector</code>.</p>
+ * {@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>
  *
  * <h2>3. Dynamic Beans (DynaBeans)</h2>
  *
diff --git a/src/test/java/org/apache/commons/beanutils/TestEnum.java 
b/src/test/java/org/apache/commons/beanutils/TestEnum.java
new file mode 100644
index 00000000..643a9d28
--- /dev/null
+++ b/src/test/java/org/apache/commons/beanutils/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.beanutils;
+
+/**
+ * An {@code enum} test fixture.
+ */
+public enum TestEnum {
+
+    /** Test fixture. */
+    A,
+
+    /** Test fixture. */
+    B,
+
+    /** Test fixture. */
+    C
+}
diff --git 
a/src/test/java/org/apache/commons/beanutils/bugs/EnumDeclaringClassTest.java 
b/src/test/java/org/apache/commons/beanutils/bugs/EnumDeclaringClassTest.java
new file mode 100644
index 00000000..15edf16d
--- /dev/null
+++ 
b/src/test/java/org/apache/commons/beanutils/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.beanutils.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.beanutils.BeanUtilsBean;
+import org.apache.commons.beanutils.PropertyUtilsBean;
+import org.apache.commons.beanutils.SuppressPropertiesBeanIntrospector;
+import org.apache.commons.beanutils.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("class " + 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"));
+    }
+}

Reply via email to