Skip to content

Commit 28ad955

Browse files
committed
Add org.apache.commons.beanutils.SuppressPropertiesBeanIntrospector.SUPPRESS_DECLARING_CLASS
1 parent e6c2fb2 commit 28ad955

File tree

7 files changed

+180
-18
lines changed

7 files changed

+180
-18
lines changed

pom.xml

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
<modelVersion>4.0.0</modelVersion>
2525
<groupId>commons-beanutils</groupId>
2626
<artifactId>commons-beanutils</artifactId>
27-
<version>1.10.2-SNAPSHOT</version>
27+
<version>1.11.0-SNAPSHOT</version>
2828
<name>Apache Commons BeanUtils</name>
2929

3030
<inceptionYear>2000</inceptionYear>
@@ -38,8 +38,8 @@
3838
<commons.componentid>beanutils</commons.componentid>
3939
<commons.main.branch>1.X</commons.main.branch>
4040
<commons.release.branch>release-1.x</commons.release.branch>
41-
<commons.release.version>1.10.1</commons.release.version>
42-
<commons.release.next>1.10.2</commons.release.next>
41+
<commons.release.version>1.11.0</commons.release.version>
42+
<commons.release.next>1.11.1</commons.release.next>
4343
<commons.jira.id>BEANUTILS</commons.jira.id>
4444
<commons.jira.pid>12310460</commons.jira.pid>
4545
<!-- Limit memory size see BEANUTILS-291; allow command-line override -->
@@ -99,6 +99,11 @@
9999
<artifactId>junit-vintage-engine</artifactId>
100100
<scope>test</scope>
101101
</dependency>
102+
<dependency>
103+
<groupId>org.junit.jupiter</groupId>
104+
<artifactId>junit-jupiter</artifactId>
105+
<scope>test</scope>
106+
</dependency>
102107
</dependencies>
103108

104109
<build>

src/changes/changes.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
<title>Release Notes</title>
2929
</properties>
3030
<body>
31-
<release version="1.10.2" date="YYYY-MM-DD" description="This is a maintenance release and requires Java 8.">
31+
<release version="1.11.0" date="YYYY-MM-DD" description="This is a maintenance release and requires Java 8.">
3232
<!-- FIX -->
3333
<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>
3434
<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 @@
3838
<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>
3939
<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>
4040
<!-- ADD -->
41+
<action type="add" dev="ggregory" due-to="Gary Gregory">Add org.apache.commons.beanutils.SuppressPropertiesBeanIntrospector.SUPPRESS_DECLARING_CLASS.</action>
4142
<!-- UPDATE -->
4243
<action dev="ggregory" type="update" due-to="Gary Gregory">Bump org.apache.commons:commons-parent from 81 to 84.</action>
4344
<action dev="ggregory" type="update" due-to="Gary Gregory">Bump commons-logging:commons-logging from 1.3.4 to 1.3.5.</action>

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1478,6 +1478,7 @@ public final void resetBeanIntrospectors() {
14781478
introspectors.clear();
14791479
introspectors.add(DefaultBeanIntrospector.INSTANCE);
14801480
introspectors.add(SuppressPropertiesBeanIntrospector.SUPPRESS_CLASS);
1481+
introspectors.add(SuppressPropertiesBeanIntrospector.SUPPRESS_DECLARING_CLASS);
14811482
}
14821483

14831484
/**

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

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,24 @@
3636
* @since 1.9.2
3737
*/
3838
public class SuppressPropertiesBeanIntrospector implements BeanIntrospector {
39+
3940
/**
40-
* A specialized instance which is configured to suppress the special {@code class}
41-
* properties of Java beans. Unintended access to the property {@code class} (which is
42-
* common to all Java objects) can be a security risk because it also allows access to
43-
* the class loader. Adding this instance as {@code BeanIntrospector} to an instance
44-
* of {@code PropertyUtilsBean} suppresses the {@code class} property; it can then no
45-
* longer be accessed.
41+
* A specialized instance which is configured to suppress the special {@code class} properties of Java beans. Unintended access to the property
42+
* {@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
43+
* {@code BeanIntrospector} to an instance of {@code PropertyUtilsBean} suppresses the {@code class} property; it can then no longer be accessed.
44+
*/
45+
public static final SuppressPropertiesBeanIntrospector SUPPRESS_CLASS = new SuppressPropertiesBeanIntrospector(Collections.singleton("class"));
46+
47+
/**
48+
* A specialized instance which is configured to suppress the special {@code class} properties of Java beans. Unintended access to the call for
49+
* {@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
50+
* instance as {@code BeanIntrospector} to an instance of {@code PropertyUtilsBean} suppresses the {@code class} property; it can then no longer be
51+
* accessed.
52+
*
53+
* @since 1.11.0
4654
*/
47-
public static final SuppressPropertiesBeanIntrospector SUPPRESS_CLASS =
48-
new SuppressPropertiesBeanIntrospector(Collections.singleton("class"));
55+
public static final SuppressPropertiesBeanIntrospector SUPPRESS_DECLARING_CLASS = new SuppressPropertiesBeanIntrospector(
56+
Collections.singleton("declaringClass"));
4957

5058
/** A set with the names of the properties to be suppressed. */
5159
private final Set<String> propertyNames;

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

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -420,20 +420,26 @@
420420
* then be removed if they have been detected by other <code>BeanIntrospector</code>
421421
* instances during processing of a bean class.</p>
422422
*
423-
* <p>A good use case for suppressing properties is the special <code>class</code>
423+
* <p>A good use case for suppressing properties is the special {@code class}
424424
* property which is per default available for all beans; it is generated from the
425-
* <code>getClass()</code> method inherited from <code>Object</code> which follows the
425+
* {@code getClass()} method inherited from {@code Object} which follows the
426426
* naming conventions for property get methods. Exposing this property in an
427427
* uncontrolled way can lead to a security vulnerability as it allows access to
428428
* the class loader. More information can be found at
429429
* <a href="https://issues.apache.org/jira/browse/BEANUTILS-463">
430430
* https://issues.apache.org/jira/browse/BEANUTILS-463</a>.</p>
431431
*
432-
* <p>Because the <code>class</code> property is undesired in many use cases
433-
* there is already an instance of <code>SuppressPropertiesBeanIntrospector</code>
432+
* <p>Because the {@code class} property is undesired in many use cases
433+
* there is already an instance of {@code SuppressPropertiesBeanIntrospector}
434434
* which is configured to suppress this property. It can be obtained via the
435-
* <code>SUPPRESS_CLASS</code> constant of
436-
* <code>SuppressPropertiesBeanIntrospector</code>.</p>
435+
* {@code SUPPRESS_CLASS} constant of
436+
* {@code SuppressPropertiesBeanIntrospector}.</p>
437+
*
438+
* <p>Another problematic property is the {@code enum} "declaredClass" property,
439+
* through which you can also access that class' class loader. The {@code SuppressPropertiesBeanIntrospector}
440+
* provides {@code SUPPRESS_DECLARING_CLASS} to workaround this issue.</p>
441+
*
442+
* <p>Both {@code SUPPRESS_CLASS} and {@code SUPPRESS_DECLARING_CLASS} are enabled by default.</p>
437443
*
438444
* <h2>3. Dynamic Beans (DynaBeans)</h2>
439445
*
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.beanutils;
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+
}
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.beanutils.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.beanutils.BeanUtilsBean;
26+
import org.apache.commons.beanutils.PropertyUtilsBean;
27+
import org.apache.commons.beanutils.SuppressPropertiesBeanIntrospector;
28+
import org.apache.commons.beanutils.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("class " + 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)