Skip to content

Commit bd20740

Browse files
committed
Add org.apache.commons.beanutils.SuppressPropertiesBeanIntrospector.SUPPRESS_DECLARING_CLASS
1 parent 4d84921 commit bd20740

File tree

6 files changed

+178
-2
lines changed

6 files changed

+178
-2
lines changed

src/changes/changes.xml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
<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>
5555
<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>
5656
<!-- ADD -->
57+
<action type="add" dev="ggregory" due-to="Gary Gregory">Add org.apache.commons.beanutils.SuppressPropertiesBeanIntrospector.SUPPRESS_DECLARING_CLASS.</action>
5758
<!-- UPDATE -->
5859
<action type="update" dev="ggregory" due-to="Gary Gregory, Dependabot">Bump org.apache.commons:commons-parent from 78 to 84 #348.</action>
5960
<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 @@
258259
Do not implement Serializable.
259260
</action>
260261
</release>
262+
<release version="1.11.0" date="YYYY-MM-DD" description="This is a maintenance release and requires Java 8.">
263+
<!-- FIX -->
264+
<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>
265+
<action type="fix" dev="ggregory" due-to="Gary Gregory">MappedMethodReference.get() now throws IllegalStateException instead of RuntimeException to wrap cases of NoSuchMethodException.</action>
266+
<action type="fix" dev="ggregory" due-to="Gary Gregory">ResultSetIterator.get(String) now throws IllegalArgumentException instead of RuntimeException to wrap cases of SQLException.</action>
267+
<action type="fix" dev="ggregory" due-to="Gary Gregory">ResultSetIterator.hasNext() now throws IllegalStateException instead of RuntimeException to wrap cases of SQLException.</action>
268+
<action type="fix" dev="ggregory" due-to="Gary Gregory">ResultSetIterator.next() now throws IllegalStateException instead of RuntimeException to wrap cases of SQLException.</action>
269+
<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>
270+
<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>
271+
<!-- ADD -->
272+
<action type="add" dev="ggregory" due-to="Gary Gregory">Add org.apache.commons.beanutils.SuppressPropertiesBeanIntrospector.SUPPRESS_DECLARING_CLASS.</action>
273+
<!-- UPDATE -->
274+
<action dev="ggregory" type="update" due-to="Gary Gregory">Bump org.apache.commons:commons-parent from 81 to 84.</action>
275+
<action dev="ggregory" type="update" due-to="Gary Gregory">Bump commons-logging:commons-logging from 1.3.4 to 1.3.5.</action>
276+
</release>
261277
<release version="1.10.1" date="2025-01-31" description="This is a maintenance release and requires Java 8.">
262278
<!-- FIX -->
263279
<action type="fix" issue="BEANUTILS-541" dev="ggregory" due-to="Sergey Chernov">FluentPropertyBeanIntrospector concurrency issue (backport to 1.X) #325.</action>

src/main/java/org/apache/commons/beanutils2/PropertyUtilsBean.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1201,6 +1201,7 @@ public final void resetBeanIntrospectors() {
12011201
introspectors.clear();
12021202
introspectors.add(DefaultBeanIntrospector.INSTANCE);
12031203
introspectors.add(SuppressPropertiesBeanIntrospector.SUPPRESS_CLASS);
1204+
introspectors.add(SuppressPropertiesBeanIntrospector.SUPPRESS_DECLARING_CLASS);
12041205
}
12051206

12061207
/**

src/main/java/org/apache/commons/beanutils2/SuppressPropertiesBeanIntrospector.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,26 @@
3535
* @since 1.9.2
3636
*/
3737
public class SuppressPropertiesBeanIntrospector implements BeanIntrospector {
38+
3839
/**
3940
* A specialized instance which is configured to suppress the special {@code class} properties of Java beans. Unintended access to the property
4041
* {@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
4142
* {@code BeanIntrospector} to an instance of {@code PropertyUtilsBean} suppresses the {@code class} property; it can then no longer be accessed.
4243
*/
4344
public static final SuppressPropertiesBeanIntrospector SUPPRESS_CLASS = new SuppressPropertiesBeanIntrospector(Collections.singleton("class"));
4445

45-
/** A set with the names of the properties to be suppressed. */
46+
/**
47+
* A specialized instance which is configured to suppress the special {@code class} properties of Java beans. Unintended access to the call for
48+
* {@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
49+
* instance as {@code BeanIntrospector} to an instance of {@code PropertyUtilsBean} suppresses the {@code class} property; it can then no longer be
50+
* accessed.
51+
*
52+
* @since 2.0.0-M2
53+
*/
54+
public static final SuppressPropertiesBeanIntrospector SUPPRESS_DECLARING_CLASS = new SuppressPropertiesBeanIntrospector(
55+
Collections.singleton("declaringClass"));
56+
57+
/** A set with the names of the properties to be suppressed. */
4658
private final Set<String> propertyNames;
4759

4860
/**
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* https://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.commons.beanutils2;
19+
20+
/**
21+
* An {@code enum} test fixture.
22+
*/
23+
public enum TestEnum {
24+
25+
/** Test fixture. */
26+
A,
27+
28+
/** Test fixture. */
29+
B,
30+
31+
/** Test fixture. */
32+
C
33+
}

src/main/java/org/apache/commons/beanutils2/package-info.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,7 @@
424424
*
425425
* <p>A good use case for suppressing properties is the special {@code class}
426426
* property which is per default available for all beans; it is generated from the
427-
* {@code getClass()</code> method inherited from <code>Object} which follows the
427+
* {@code getClass()} method inherited from {@code Object} which follows the
428428
* naming conventions for property get methods. Exposing this property in an
429429
* uncontrolled way can lead to a security vulnerability as it allows access to
430430
* the class loader. More information can be found at
@@ -437,6 +437,12 @@
437437
* {@code SUPPRESS_CLASS} constant of
438438
* {@code SuppressPropertiesBeanIntrospector}.</p>
439439
*
440+
* <p>Another problematic property is the {@code enum} "declaredClass" property,
441+
* through which you can also access that class' class loader. The {@code SuppressPropertiesBeanIntrospector}
442+
* provides {@code SUPPRESS_DECLARING_CLASS} to workaround this issue.</p>
443+
*
444+
* <p>Both {@code SUPPRESS_CLASS} and {@code SUPPRESS_DECLARING_CLASS} are enabled by default.</p>
445+
*
440446
* <a id="dynamic"></a>
441447
* <h2>3. Dynamic Beans (DynaBeans)</h2>
442448
*
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* https://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.commons.beanutils2.bugs;
19+
20+
import static org.junit.jupiter.api.Assertions.assertEquals;
21+
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
22+
import static org.junit.jupiter.api.Assertions.assertNotNull;
23+
import static org.junit.jupiter.api.Assertions.assertThrows;
24+
25+
import org.apache.commons.beanutils2.BeanUtilsBean;
26+
import org.apache.commons.beanutils2.PropertyUtilsBean;
27+
import org.apache.commons.beanutils2.SuppressPropertiesBeanIntrospector;
28+
import org.apache.commons.beanutils2.TestEnum;
29+
import org.junit.jupiter.api.Test;
30+
31+
public class EnumDeclaringClassTest {
32+
33+
public static class Fixture {
34+
35+
String name = "default";
36+
TestEnum testEnum = TestEnum.A;
37+
38+
public String getName() {
39+
return name;
40+
}
41+
42+
public TestEnum getTestEnum() {
43+
return testEnum;
44+
}
45+
46+
public void setName(final String name) {
47+
this.name = name;
48+
}
49+
50+
public void setTestEnum(final TestEnum day) {
51+
this.testEnum = day;
52+
}
53+
}
54+
55+
/**
56+
* Allow opt-out to make your app less secure but allow access to "declaringClass".
57+
*/
58+
@Test
59+
public void testAllowAccessToClassPropertyFromBeanUtilsBean() throws ReflectiveOperationException {
60+
final BeanUtilsBean bub = new BeanUtilsBean();
61+
final PropertyUtilsBean propertyUtilsBean = bub.getPropertyUtils();
62+
propertyUtilsBean.removeBeanIntrospector(SuppressPropertiesBeanIntrospector.SUPPRESS_DECLARING_CLASS);
63+
final Fixture fixture = new Fixture();
64+
final String string = bub.getProperty(fixture, "testEnum.declaringClass");
65+
assertEquals(TestEnum.class.getName(), string);
66+
final Class<TestEnum> teClass = assertInstanceOf(Class.class, propertyUtilsBean.getNestedProperty(fixture, "testEnum.declaringClass"));
67+
final ClassLoader classLoader = teClass.getClassLoader();
68+
assertNotNull(classLoader);
69+
assertNotNull(bub.getProperty(fixture, "testEnum.declaringClass.classLoader"));
70+
assertInstanceOf(ClassLoader.class, propertyUtilsBean.getNestedProperty(fixture, "testEnum.declaringClass.classLoader"));
71+
}
72+
73+
/**
74+
* Allow opt-out to make your app less secure but allow access to "declaringClass".
75+
*/
76+
@Test
77+
public void testAllowAccessToClassPropertyFromPropertyUtilsBean() throws ReflectiveOperationException {
78+
final PropertyUtilsBean propertyUtilsBean = new PropertyUtilsBean();
79+
propertyUtilsBean.removeBeanIntrospector(SuppressPropertiesBeanIntrospector.SUPPRESS_DECLARING_CLASS);
80+
final Fixture fixture = new Fixture();
81+
final Object cls = propertyUtilsBean.getNestedProperty(fixture, "testEnum.declaringClass");
82+
final Class<TestEnum> teClass = assertInstanceOf(Class.class, cls);
83+
final ClassLoader classLoader = teClass.getClassLoader();
84+
assertNotNull(classLoader);
85+
assertInstanceOf(ClassLoader.class, propertyUtilsBean.getNestedProperty(fixture, "testEnum.declaringClass.classLoader"));
86+
}
87+
88+
/**
89+
* By default opt-in to security that does not allow access to "declaringClass".
90+
*/
91+
@Test
92+
public void testSuppressClassPropertyByDefaultFromBeanUtilsBean() throws ReflectiveOperationException {
93+
final Fixture fixture = new Fixture();
94+
final BeanUtilsBean bub = new BeanUtilsBean();
95+
assertThrows(NoSuchMethodException.class, () -> bub.getProperty(fixture, "testEnum.declaringClass.classLoader"));
96+
assertThrows(NoSuchMethodException.class, () -> bub.getPropertyUtils().getNestedProperty(fixture, "testEnum.declaringClass.classLoader"));
97+
}
98+
99+
/**
100+
* By default opt-in to security that does not allow access to "declaringClass".
101+
*/
102+
@Test
103+
public void testSuppressClassPropertyByDefaultFromPropertyUtilsBean() throws ReflectiveOperationException {
104+
final Fixture fixture = new Fixture();
105+
final PropertyUtilsBean propertyUtilsBean = new PropertyUtilsBean();
106+
assertThrows(NoSuchMethodException.class, () -> propertyUtilsBean.getNestedProperty(fixture, "testEnum.declaringClass.classLoader"));
107+
}
108+
}

0 commit comments

Comments
 (0)