diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 000000000..815a8bffc --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,77 @@ +# 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. + +name: "CodeQL" +on: [push, pull_request] + +permissions: + contents: read + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'java' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Learn more about CodeQL language support at https://git.io/codeql-language-support + + steps: + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2 + with: + persist-credentials: false + - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@51f77329afa6477de8c49fc9c7046c15b9a4e79d # 3.29.5 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@51f77329afa6477de8c49fc9c7046c15b9a4e79d # 3.29.5 + + # â„šī¸ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # âœī¸ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@51f77329afa6477de8c49fc9c7046c15b9a4e79d # 3.29.5 diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 000000000..33573cf94 --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,31 @@ +# 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. + +name: 'Dependency Review' +on: [pull_request] + +permissions: + contents: read + +jobs: + dependency-review: + runs-on: ubuntu-latest + steps: + - name: 'Checkout Repository' + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: 'Dependency Review PR' + uses: actions/dependency-review-action@da24556b548a50705dd671f47852072ea4c105d9 # v4.7.1 diff --git a/.github/workflows/maven.yaml b/.github/workflows/maven.yaml new file mode 100644 index 000000000..7cc818376 --- /dev/null +++ b/.github/workflows/maven.yaml @@ -0,0 +1,60 @@ +# 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. + +name: Java CI + +on: + push: + branches: [ ${{ github.event.repository.default_branch }} ] + pull_request: + branches: [ ${{ github.event.repository.default_branch }} ] + +permissions: + contents: read + +jobs: + build: + + runs-on: ubuntu-latest + continue-on-error: ${{ matrix.experimental }} + strategy: + matrix: + java: [ 8, 11, 17, 21 ] + experimental: [false] + include: + - java: 24 + experimental: true + - java: 25-ea + experimental: true + + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + - name: Set up JDK ${{ matrix.java }} + uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 + with: + distribution: 'temurin' + java-version: ${{ matrix.java }} + - name: Build with Maven + run: mvn --errors --show-version --batch-mode --no-transfer-progress verify -e + +# For Java 11, you can be more strict: -DadditionalJOption=-Xdoclint/package:-org.apache.commons.configuration2.plist diff --git a/.github/workflows/publish-github-packages.yaml b/.github/workflows/publish-github-packages.yaml new file mode 100644 index 000000000..b01404aca --- /dev/null +++ b/.github/workflows/publish-github-packages.yaml @@ -0,0 +1,20 @@ +name: Publish package to GitHub Packages +on: + release: + types: [created] +jobs: + publish: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 + with: + java-version: '24' + distribution: 'temurin' + - name: Publish package + run: mvn --batch-mode deploy + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml new file mode 100644 index 000000000..19cb2ca3a --- /dev/null +++ b/.github/workflows/scorecards-analysis.yml @@ -0,0 +1,69 @@ +# 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. + +name: "Scorecards supply-chain security" + +on: + branch_protection_rule: + schedule: + - cron: "30 1 * * 6" # Weekly on Saturdays + push: + branches: [ $default-branch ] + +permissions: read-all + +jobs: + + analysis: + + name: "Scorecards analysis" + runs-on: ubuntu-latest + permissions: + # Needed to upload the results to the code-scanning dashboard. + security-events: write + actions: read + id-token: write # This is required for requesting the JWT + contents: read # This is required for actions/checkout + + steps: + + - name: "Checkout code" + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + + - name: "Run analysis" + uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # 2.4.2 + with: + results_file: results.sarif + results_format: sarif + # A read-only PAT token, which is sufficient for the action to function. + # The relevant discussion: https://github.com/ossf/scorecard-action/issues/188 + repo_token: ${{ secrets.GITHUB_TOKEN }} + # Publish the results for public repositories to enable scorecard badges. + # For more details: https://github.com/ossf/scorecard-action#publishing-results + publish_results: true + + - name: "Upload artifact" + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + + - name: "Upload to code-scanning" + uses: github/codeql-action/upload-sarif@51f77329afa6477de8c49fc9c7046c15b9a4e79d # 3.29.5 + with: + sarif_file: results.sarif diff --git a/pom.xml b/pom.xml index 1a4c70d26..8f6d44bfa 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ 4.0.0 commons-beanutils commons-beanutils - 1.9.4 + 1.9.5 Apache Commons BeanUtils 2000 @@ -32,10 +32,10 @@ https://commons.apache.org/proper/commons-beanutils/ - 1.6 - 1.6 + 1.8 + 1.8 beanutils - 1.9.4 + 1.9.5 BEANUTILS 12310460 @@ -90,6 +90,11 @@ Apache Commons Beanutils Site scm:svn:https://svn.apache.org/repos/infra/websites/production/commons/content/proper/commons-beanutils + + github + GitHub Packages + https://maven.pkg.github.com/apache/commons-beanutils + diff --git a/src/changes/changes.xml b/src/changes/changes.xml index fc21a894e..72afad444 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -28,6 +28,10 @@ Release Notes + + Add org.apache.commons.beanutils.SuppressPropertiesBeanIntrospector.SUPPRESS_DECLARING_CLASS. + propertyNames; 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 000000000..d66f2d977 --- /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 000000000..509c50d49 --- /dev/null +++ b/src/test/java/org/apache/commons/beanutils/bugs/EnumDeclaringClassTest.java @@ -0,0 +1,127 @@ +/* + * 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.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +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.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 Object cls = propertyUtilsBean.getNestedProperty(fixture, "testEnum.declaringClass"); + assertTrue(cls instanceof Class); + final Class teClass = (Class) cls; + final ClassLoader classLoader = teClass.getClassLoader(); + assertNotNull(classLoader); + assertNotNull(bub.getProperty(fixture, "testEnum.declaringClass.classLoader")); + final Object classLoaderObj = propertyUtilsBean.getNestedProperty(fixture, "testEnum.declaringClass.classLoader"); + assertTrue(classLoaderObj instanceof 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"); + assertTrue(cls instanceof Class); + final Class teClass = (Class) cls; + final ClassLoader classLoader = teClass.getClassLoader(); + assertNotNull(classLoader); + final Object classLoaderObj = propertyUtilsBean.getNestedProperty(fixture, "testEnum.declaringClass.classLoader"); + assertTrue(classLoaderObj instanceof 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(); + try { + bub.getProperty(fixture, "testEnum.declaringClass.classLoader"); + assertTrue("Expected NoSuchMethodException", false); + } catch (NoSuchMethodException e) { + // Expected + } + try { + bub.getPropertyUtils().getNestedProperty(fixture, "testEnum.declaringClass.classLoader"); + assertTrue("Expected NoSuchMethodException", false); + } catch (NoSuchMethodException e) { + // Expected + } + } + + /** + * 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(); + try { + propertyUtilsBean.getNestedProperty(fixture, "testEnum.declaringClass.classLoader"); + assertTrue("Expected NoSuchMethodException", false); + } catch (NoSuchMethodException e) { + // Expected + } + } +}