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

garydgregory 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 54a3d04a Do not resolve suppressed mapped properties in 
getPropertyDescriptor (#413)
54a3d04a is described below

commit 54a3d04a16508ecab241ad65243805982a4aec02
Author: Naveed Khan <[email protected]>
AuthorDate: Sat Jul 11 18:55:48 2026 +0000

    Do not resolve suppressed mapped properties in getPropertyDescriptor (#413)
---
 .../commons/beanutils2/PropertyUtilsBean.java      | 24 ++++++++++++++++++++++
 .../commons/beanutils2/PropertyUtilsTest.java      | 21 +++++++++++++++++++
 2 files changed, 45 insertions(+)

diff --git a/src/main/java/org/apache/commons/beanutils2/PropertyUtilsBean.java 
b/src/main/java/org/apache/commons/beanutils2/PropertyUtilsBean.java
index 2e4d6ced..7232d88d 100644
--- a/src/main/java/org/apache/commons/beanutils2/PropertyUtilsBean.java
+++ b/src/main/java/org/apache/commons/beanutils2/PropertyUtilsBean.java
@@ -675,6 +675,13 @@ public class PropertyUtilsBean {
             return result;
         }
 
+        // A name removed by a SuppressPropertiesBeanIntrospector is absent 
from data above, but the
+        // mapped-descriptor fallback below rebuilds descriptors straight from 
the class methods and never
+        // consults the introspectors, so a suppressed mapped property would 
still be resolved. Keep it hidden.
+        if (isPropertySuppressed(name)) {
+            return null;
+        }
+
         Map mappedDescriptors = getMappedPropertyDescriptors(bean);
         if (mappedDescriptors == null) {
             mappedDescriptors = new ConcurrentHashMap<Class<?>, Map>();
@@ -698,6 +705,23 @@ public class PropertyUtilsBean {
         return result;
     }
 
+    /**
+     * Tests whether a property name has been removed by a registered {@link 
SuppressPropertiesBeanIntrospector}. The mapped-descriptor fallback in
+     * {@link #getPropertyDescriptor(Object, String)} bypasses the 
introspection pipeline, so suppressed mapped property names must be filtered 
explicitly.
+     *
+     * @param name The property name to test.
+     * @return {@code true} if the name is suppressed by an introspector.
+     */
+    private boolean isPropertySuppressed(final String name) {
+        for (final BeanIntrospector introspector : introspectors) {
+            if (introspector instanceof SuppressPropertiesBeanIntrospector
+                    && ((SuppressPropertiesBeanIntrospector) 
introspector).getSuppressedProperties().contains(name)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     /**
      * <p>
      * Retrieve the property descriptors for the specified class, 
introspecting and caching them the first time a particular bean class is 
encountered.
diff --git a/src/test/java/org/apache/commons/beanutils2/PropertyUtilsTest.java 
b/src/test/java/org/apache/commons/beanutils2/PropertyUtilsTest.java
index b3b5d1bd..68338950 100644
--- a/src/test/java/org/apache/commons/beanutils2/PropertyUtilsTest.java
+++ b/src/test/java/org/apache/commons/beanutils2/PropertyUtilsTest.java
@@ -294,6 +294,27 @@ class PropertyUtilsTest {
         PropertyUtils.removeBeanIntrospector(bi);
     }
 
+    /**
+     * A mapped property removed by a {@link 
SuppressPropertiesBeanIntrospector} must stay hidden. The mapped-descriptor 
fallback in
+     * {@code getPropertyDescriptor} rebuilt the descriptor directly from the 
class methods without consulting the introspectors, so a suppressed mapped
+     * property was still readable and writable through {@code name(key)} 
access.
+     */
+    @Test
+    void testCustomIntrospectionSuppressedMappedProperty() throws Exception {
+        final PropertyUtilsBean pub = new PropertyUtilsBean();
+        pub.addBeanIntrospector(new 
SuppressPropertiesBeanIntrospector(Arrays.asList("mappedProperty")));
+
+        assertNull(pub.getPropertyDescriptor(bean, "mappedProperty"), 
"Suppressed mapped property should have no descriptor");
+        assertThrows(NoSuchMethodException.class, () -> pub.getProperty(bean, 
"mappedProperty(First Key)"), "Suppressed mapped property must not be 
readable");
+        assertThrows(NoSuchMethodException.class, () -> pub.setProperty(bean, 
"mappedProperty(First Key)", "changed"),
+                "Suppressed mapped property must not be writable");
+        assertEquals("First Value", bean.getMappedProperty("First Key"), 
"Suppressed mapped property must be unchanged");
+
+        // A mapped property that is not suppressed is still accessible.
+        final PropertyUtilsBean unsuppressed = new PropertyUtilsBean();
+        assertEquals("First Value", unsuppressed.getProperty(bean, 
"mappedProperty(First Key)"), "Mapped property should be readable when not 
suppressed");
+    }
+
     /**
      * Test the describe() method.
      */

Reply via email to