This is an automated email from the ASF dual-hosted git repository.
garydgregory pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/commons-secure-xml.git
The following commit(s) were added to refs/heads/main by this push:
new 87098ea Document JAXB (#103)
87098ea is described below
commit 87098eab14b6393dc3d79e24a0e758b7c1603c6a
Author: Gary Gregory <[email protected]>
AuthorDate: Fri Sep 25 09:25:18 2026 -0400
Document JAXB (#103)
* Document JAXB
* Document JAXB
* Exclude JAXBExamples from Android tests.
* Checkstyle
* Convert JaxbExample into JaxbTest
* Update comments and docs.
* Refactor pom.xml to extarct dependency versions into maven properties
* Remove old file.
* Fix Graal
* Fix Graal
---
android-tests/build.gradle.kts | 4 +-
pom.xml | 19 +++++
src/main/javadoc/overview.html | 67 +++++++++++++++
.../org/apache/commons/xml/secure/JaxbTest.java | 95 ++++++++++++++++++++++
4 files changed, 183 insertions(+), 2 deletions(-)
diff --git a/android-tests/build.gradle.kts b/android-tests/build.gradle.kts
index f323b98..cfa0490 100644
--- a/android-tests/build.gradle.kts
+++ b/android-tests/build.gradle.kts
@@ -103,9 +103,9 @@ android {
}
}
-// ShadingFootprintTest is a JVM-only build check (it uses jdependency to read
target/classes); exclude it from the Android test compile.
+// These are JVM-only test classes; exclude them from the Android test compile.
tasks.withType<JavaCompile>().configureEach {
- exclude("**/ShadingFootprintTest.java")
+ exclude("**/ShadingFootprintTest.java", "**/JaxbTest.java")
}
// Skip JAXP groups whose factories Android does not ship
diff --git a/pom.xml b/pom.xml
index a3108d6..7666d35 100644
--- a/pom.xml
+++ b/pom.xml
@@ -73,6 +73,9 @@ limitations under the License.
<commons.xerces.version>2.12.2</commons.xerces.version>
<!-- Test-only: computes each secure class's shade closure, mirroring
maven-shade minimizeJar, for ShadingFootprintTest. -->
<commons.jdependency.version>2.16</commons.jdependency.version>
+ <!-- Test-only JAXB dependencies. -->
+ <commons.jaxb.api.version>2.3.1</commons.jaxb.api.version>
+ <commons.jaxb.runtime.version>2.3.9</commons.jaxb.runtime.version>
<!--
jacoco-maven-plugin: Should only get better.
@@ -116,6 +119,19 @@ limitations under the License.
<version>${commons.jdependency.version}</version>
<scope>test</scope>
</dependency>
+ <!-- JAXB examples. -->
+ <dependency>
+ <groupId>javax.xml.bind</groupId>
+ <artifactId>jaxb-api</artifactId>
+ <version>${commons.jaxb.api.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.glassfish.jaxb</groupId>
+ <artifactId>jaxb-runtime</artifactId>
+ <version>${commons.jaxb.runtime.version}</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
<defaultGoal>clean checkstyle:check spotbugs:check pmd:check
javadoc:javadoc verify</defaultGoal>
@@ -716,6 +732,9 @@ limitations under the License.
<buildArgs>
<!-- The JUnit platform feature discovers tests while the
image is built, which initializes TestTag. -->
<buildArg>--initialize-at-build-time=org.junit.platform.engine.TestTag</buildArg>
+ <!-- LocatorImpl has no static fields or initializer and is
safe to retain in the image heap. -->
+
<buildArg>--initialize-at-build-time=org.xml.sax.helpers.LocatorImpl</buildArg>
+
<buildArg>--initialize-at-build-time=org.xml.sax.helpers.DefaultHandler</buildArg>
<!-- Embed the fixtures explicitly: the agent records only
resources a run actually opened. -->
<buildArg>-H:IncludeResources=leaked/.*</buildArg>
</buildArgs>
diff --git a/src/main/javadoc/overview.html b/src/main/javadoc/overview.html
index f2df1c0..addc991 100644
--- a/src/main/javadoc/overview.html
+++ b/src/main/javadoc/overview.html
@@ -529,6 +529,73 @@ <h1>
<a href="../threat_model.html">Threat Model</a> documents the resulting
contract.
</p>
</section>
+ <section id="jaxb">
+ <h1>
+ <img src="org/apache/commons/xml/secure/doc-files/leaf.svg"
style="height: 1em; padding-right: 0.25em" alt="leaf">Migrating JAXB
+ </h1>
+ This section shows you how to secure XML parsing and JAXB unmarshalling.
+ <p>
+ JAXB does not provide a portable guarantee that external entity
resolution is disabled or that Billion Laughs entity-expansion payloads are
bounded by default.
+ Below are the two recommended approaches for integrating <strong>Apache
Commons Secure XML</strong> to harden your unmarshalling pipeline.
+ </p>
+ <h2>Option 1: SAX-based Secure Unmarshalling (Recommended)</h2>
+ <p>
+ This approach uses
+ <code>SecureSAXParserFactory</code>
+ to construct a hardened, secure
+ <code>XMLReader</code>
+ which is then wrapped inside a
+ <code>SAXSource</code>
+ .
+ </p>
+ <pre>
+public MyJaxbModel unmarshalSecurelyWithSax(InputStream xmlStream) throws
Exception {
+ JAXBContext context = JAXBContext.newInstance(MyJaxbModel.class);
+ Unmarshaller unmarshaller = context.createUnmarshaller();
+
+ // Create a secure SAXParserFactory via Apache Commons Secure XML
+ SAXParserFactory spf = SecureSAXParserFactory.newDefaultNSInstance();
+
+ // Generate a hardened XMLReader and wrap the input source
+ XMLReader xmlReader = spf.newSAXParser().getXMLReader();
+ SAXSource source = new SAXSource(xmlReader, new InputSource(xmlStream));
+
+ // With the default resolver configuration, external DTDs and entities are
prevented from being fetched or resolved, protecting against XXE attacks,
protecting against XXE attacks.
+ // Entity-expansion limits use FEATURE_SECURE_PROCESSING on JDK parsers;
+ // Android support is implementation-dependent and best-effort.
+ return (MyJaxbModel) unmarshaller.unmarshal(source);
+}
+</pre>
+ <h2>Option 2: StAX-based Secure Unmarshalling</h2>
+ <p>
+ This approach uses
+ <code>SecureXMLInputFactory</code>
+ to instantiate a secure
+ <code>XMLStreamReader</code>
+ cursor stream.
+ </p>
+ <pre>
+public MyJaxbModel unmarshalSecurelyWithStax(InputStream xmlStream) throws
Exception {
+ JAXBContext context = JAXBContext.newInstance(MyJaxbModel.class);
+ Unmarshaller unmarshaller = context.createUnmarshaller();
+
+ // Create a secure XMLInputFactory via Apache Commons Secure XML
+ XMLInputFactory xif = SecureXMLInputFactory.newDefaultFactory();
+
+ // Create a hardened cursor reader
+ XMLStreamReader xmlReader = xif.createXMLStreamReader(xmlStream);
+
+ try {
+ // With the default resolver configuration, external DTDs and entities
are prevented from being fetched or resolved, protecting against XXE attacks,
protecting against XXE attacks.
+ // Entity-expansion protection is implementation-dependent and
best-effort
+ // because StAX exposes no secure-processing feature.
+ return (MyJaxbModel) unmarshaller.unmarshal(xmlReader);
+ } finally {
+ xmlReader.close();
+ }
+}
+</pre>
+ </section>
<section id="openrewrite-recipe">
<h1>
<img src="org/apache/commons/xml/secure/doc-files/leaf.svg"
style="height: 1em; padding-right: 0.25em" alt="leaf">Migrating to Apache
Commons Secure
diff --git a/src/test/java/org/apache/commons/xml/secure/JaxbTest.java
b/src/test/java/org/apache/commons/xml/secure/JaxbTest.java
new file mode 100644
index 0000000..42546b6
--- /dev/null
+++ b/src/test/java/org/apache/commons/xml/secure/JaxbTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.xml.secure;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.transform.sax.SAXSource;
+
+import org.junit.jupiter.api.Test;
+import org.xml.sax.InputSource;
+import org.xml.sax.XMLReader;
+
+/**
+ * Tests JAXB integration.
+ * <p>
+ * This class' two public methods serve as examples for the Javadoc {@code
overview.html} file.
+ * </p>
+ */
+public class JaxbTest {
+
+ @XmlRootElement
+ static class MyJaxbModel {
+ // JAXB model fields and methods
+ }
+
+ @Test
+ void testUnmarshalSecurelyWithSax() throws Exception {
+ try (InputStream xmlStream = new
ByteArrayInputStream("<myJaxbModel/>".getBytes(StandardCharsets.UTF_8))) {
+ assertNotNull(new JaxbTest().unmarshalSecurelyWithSax(xmlStream));
+ }
+ }
+
+ @Test
+ void testUnmarshalSecurelyWithStax() throws Exception {
+ try (InputStream xmlStream = new
ByteArrayInputStream("<myJaxbModel/>".getBytes(StandardCharsets.UTF_8))) {
+ assertNotNull(new JaxbTest().unmarshalSecurelyWithStax(xmlStream));
+ }
+ }
+
+ public MyJaxbModel unmarshalSecurelyWithSax(final InputStream xmlStream)
throws Exception {
+ final JAXBContext context = JAXBContext.newInstance(MyJaxbModel.class);
+ final Unmarshaller unmarshaller = context.createUnmarshaller();
+ // Create a secure SAXParserFactory via Apache Commons Secure XML
+ final SAXParserFactory spf =
SecureSAXParserFactory.newDefaultNSInstance();
+ // Generate a hardened XMLReader and wrap the input source
+ final XMLReader xmlReader = spf.newSAXParser().getXMLReader();
+ final SAXSource source = new SAXSource(xmlReader, new
InputSource(xmlStream));
+ // With the default resolver configuration, external DTDs and entities
are prevented from being fetched or resolved, protecting against XXE attacks,
protecting against XXE attacks.
+ // Entity-expansion limits use FEATURE_SECURE_PROCESSING on JDK
parsers;
+ // Android support is implementation-dependent and best-effort.
+ return (MyJaxbModel) unmarshaller.unmarshal(source);
+ }
+
+ public MyJaxbModel unmarshalSecurelyWithStax(final InputStream xmlStream)
throws Exception {
+ final JAXBContext context = JAXBContext.newInstance(MyJaxbModel.class);
+ final Unmarshaller unmarshaller = context.createUnmarshaller();
+ // Create a secure XMLInputFactory via Apache Commons Secure XML
+ final XMLInputFactory xif = SecureXMLInputFactory.newDefaultFactory();
+ // Create a hardened cursor reader
+ final XMLStreamReader xmlReader = xif.createXMLStreamReader(xmlStream);
+ try {
+ // With the default resolver configuration, external DTDs and
entities are prevented from being fetched or resolved, protecting against XXE
attacks, protecting against XXE attacks.
+ // Entity-expansion protection is implementation-dependent and
best-effort
+ // because StAX exposes no secure-processing feature.
+ return (MyJaxbModel) unmarshaller.unmarshal(xmlReader);
+ } finally {
+ xmlReader.close();
+ }
+ }
+}