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-xml.git
commit d559440417151b54fccabbafec0e6e2bad95901f Author: Gary Gregory <[email protected]> AuthorDate: Sun Aug 30 07:48:52 2026 -0400 Move documentation to Javadoc to sync with the version documented. --- .../java/org/apache/commons/xml/doc-files/leaf.svg | 45 +++ .../java/org/apache/commons/xml/doc-files/logo.png | Bin 0 -> 9454 bytes .../java/org/apache/commons/xml/package-info.java | 23 +- src/main/javadoc/overview.html | 319 +++++++++++++++++++++ src/site/markdown/index.md | 230 +-------------- 5 files changed, 381 insertions(+), 236 deletions(-) diff --git a/src/main/java/org/apache/commons/xml/doc-files/leaf.svg b/src/main/java/org/apache/commons/xml/doc-files/leaf.svg new file mode 100644 index 0000000..71de588 --- /dev/null +++ b/src/main/java/org/apache/commons/xml/doc-files/leaf.svg @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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. +--> +<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 650 1000"> + <defs> + <style> + .cls-1 { + fill: #7c297d; + } + + .cls-2 { + fill: #f79a23; + } + + .cls-3 { + fill: #dd552c; + } + + .cls-4 { + fill: #d22128; + } + </style> + </defs> + <path class="cls-3" d="M276.7092915,398.1515795c25.5279479-63.1242453,54.0110775-126.1246793,84.5729347-181.9636035-45.6212286-33.8852148-89.4276433-106.9534674-107.4259055-139.3268803-6.4642564,7.3439687-10.6608099,15.8022396-12.563595,22.6835448-16.9556402,61.2214602,43.4023987,135.1498759-5.21311,108.1394499-40.5058645-22.5076023-131.7157397-71.797557-166.5067324-22.8073561,38.9647388,50.0654049,140.8777805,176.0271745,207.1364082,213.2748452Z"/> + <path class="cls-2" d="M361.2822261,216.187976c29.6137228-54.1055651,61.1725873-101.4927347,93.8913687-135.6320886,0,0-32.6340684,47.2372927-79.2457879,141.6662634,28.2289905,7.7740502,108.6249208,23.7261667,220.6090393-5.2000772,2.7531737-20.350678-10.9279818-42.734469-79.1056856-50.2283145-44.5101845-4.8872906,53.4246026-106.2822795-17.5225659-154.2363748-2.2905102-1.5509002-4.5419221-2.9193416-6.7477192-4.1444224-2.3784814-.8536468-4.8905488-1.6356133-7.5720422-2.3328667-82.8591248- [...] + <path class="cls-4" d="M210.0661969,580.0239535c18.7052902-56.0344158,41.2063761-118.989235,66.6430946-181.872374-66.2586277-37.2476707-168.1716694-163.2094403-207.1364082-213.2748452-6.9562436,9.787614-11.7099483,23.4394457-13.2934304,42.1088958-8.4973692,100.2806866,94.9567981,174.521889,74.3324318,188.0824913-27.2808561,17.9396147-81.5786546-43.0928703-102.978471-4.3138485,31.0180043,39.8477093,94.2008971,111.8472744,182.4327833,169.2696807Z"/> + <path class="cls-3" d="M496.7155649,363.7515701c-52.3819806-18.6824828,54.7376547-68.786986,89.5221309-121.9411586,4.4506926-6.7965921,9.0512622-15.5806824,10.2991504-24.7883379-111.9841185,28.9262439-192.3800488,12.9741274-220.6090393,5.2000772-24.1301828,48.8924551-51.9942555,110.5048986-81.0540854,184.7917156,30.2653616,12.9415455,153.8421334,60.7457639,328.3900879,60.9933866,29.3465509-76.4372249-76.8347245-86.5311091-126.5482445-104.255683Z"/> + <path class="cls-4" d="M230.3060964,590.1113213c30.7801562,9.5921223,132.7681363,38.244678,241.0835287,33.9308295,14.5510932-39.3980786-39.8509675-43.2427472-44.282111-74.84071-3.430878-24.4494858,143.1682907,20.5461697,190.3371613-68.3569045,2.3849978-4.4963073,4.2454264-8.7384756,5.819134-12.8372833-174.5479545-.2476227-298.1247263-48.0518411-328.3900879-60.9933866-21.2369067,54.2880239-43.0830957,115.2162467-64.5676252,183.0974549Z"/> + <path class="cls-1" d="M230.3060964,590.1113213c-13.8310324,43.6923779-27.4763477,90.3692613-40.7209052,139.6983144-4.6983154,17.4899839-9.3412414,35.3057873-13.9190036,53.5125738,102.8057868,33.9373459,197.4726056.0781966,200.6819264-41.8873386.0260655-.3323358-.0358401-.5799585-.016291-.8992615,2.4469035-44.4482789-64.1733837-19.8098179-62.5964179-46.5335229,1.5834822-26.9191966,116.3077416-.1563933,151.7862131-57.872037,2.7205918-4.4246271,4.4930492-8.3540087,5.8680069-12.0878987-10 [...] + <path class="cls-1" d="M27.6334136,410.7542728c-1.4987691,2.7173336-2.8509195,5.8582323-4.0043201,9.6116715-19.9238547,64.7533422,120.9604422,151.7405984,101.7924885,170.7032859-17.2782014,17.0859679-39.7955782-21.9602257-67.5619052-5.8321668-3.0431529,1.7724574-6.1319206,4.0075783-9.3021431,7.2592556-31.4024712,32.1714049-.4919873,124.8539837,88.6033203,174.3263973-20.7905342,69.8100589-41.489839,147.8047004-61.7525458,229.3703222,7.3504851-2.573973,16.1476081-5.1544625,18.3371143-12. [...] +</svg> \ No newline at end of file diff --git a/src/main/java/org/apache/commons/xml/doc-files/logo.png b/src/main/java/org/apache/commons/xml/doc-files/logo.png new file mode 100644 index 0000000..0c32a3e Binary files /dev/null and b/src/main/java/org/apache/commons/xml/doc-files/logo.png differ diff --git a/src/main/java/org/apache/commons/xml/package-info.java b/src/main/java/org/apache/commons/xml/package-info.java index 6f9b5ba..f41921d 100644 --- a/src/main/java/org/apache/commons/xml/package-info.java +++ b/src/main/java/org/apache/commons/xml/package-info.java @@ -14,21 +14,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + /** - * Apache Commons Secure XML provides secure-by-default JAXP factory creation for Java. A single method call returns a secure JAXP factory that can be used to - * <em>safely</em> parse XML files. + * <a href="https://commons.apache.org/xml">Apache Commons Secure XML</a> provides secure-by-default JAXP factory creation for Java. A single method call + * returns a secure JAXP factory that can be used to <em>safely</em> parse XML files. * <p> - * Every method returns <em>new, secure</em> factory instances. No caching or pooling is performed; callers on a hot path are responsible for their own - * caching. + * Every method returns <em>new, secure</em> factory instances. No caching or pooling is performed; callers on a hot path are responsible for their own caching. * </p> * <p> - * A returned factory is not necessarily an instance of the underlying implementation. It might be (and usually is) a wrapper around it, so it cannot be cast - * to the implementation's own class. Everything else about the implementation's behavior is preserved: features, properties, and attributes delegate to it, - * and only the security behavior is secure. + * A returned factory is not necessarily an instance of the underlying implementation. It might be (and usually is) a wrapper around it, so it cannot be cast to + * the implementation's own class. Everything else about the implementation's behavior is preserved: features, properties, and attributes delegate to it, and + * only the security behavior is secure. * </p> * <p> - * Preserved behavior includes the choice of internal parsers. Each TrAX, XPath, or schema implementation has its own way of instantiating them, and the - * library respects it: + * Preserved behavior includes the choice of internal parsers. Each TrAX, XPath, or schema implementation has its own way of instantiating them, and the library + * respects it: * </p> * <ul> * <li>Stock JDK factories use the JDK parsers by default, and expose the {@code jdk.xml.overrideDefaultParser} feature (and Java system property of the same @@ -83,8 +83,8 @@ * <h2>Caller-supplied URIs</h2> * <p> * A top-level URI passed directly by the caller is fetched as-is: {@code StreamSource(systemId)}, {@code DocumentBuilder.parse(String)}, or a {@code SAXSource} - * built from a system id all cause the JAXP implementation to open that URI without consulting the secure layer. Use a - * {@link javax.xml.transform.URIResolver} or {@link org.xml.sax.EntityResolver} if you need to restrict the top-level fetch. + * built from a system id all cause the JAXP implementation to open that URI without consulting the secure layer. Use a {@link javax.xml.transform.URIResolver} + * or {@link org.xml.sax.EntityResolver} if you need to restrict the top-level fetch. * </p> * <h2>Thread safety</h2> * <p> @@ -92,4 +92,5 @@ * be thread-safe</strong>. Create a new factory per thread or synchronize externally. * </p> */ + package org.apache.commons.xml; diff --git a/src/main/javadoc/overview.html b/src/main/javadoc/overview.html new file mode 100644 index 0000000..b780a87 --- /dev/null +++ b/src/main/javadoc/overview.html @@ -0,0 +1,319 @@ +<!-- +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. +--> +<html> +<head> +<title>Apache Commons Secure XML Overview</title> +</head> +<body> + <a href="https://commons.apache.org/xml"><img src="org/apache/commons/xml/doc-files/logo.png" alt="Apache Commons Secure XML"> + </a> + <section id="apache-commons-secure-xml"> + <h1> + <img src="org/apache/commons/xml/doc-files/leaf.svg" style="height: 1em; padding-right: 0.25em" alt="leaf">Apache Commons Secure XML + </h1> + <p> + <a href="https://commons.apache.org/xml">Apache Commons Secure XML</a> is part of the <a href="https://commons.apache.org/index.html">Apache Commons</a> project. + </p> + <p>Apache Commons Secure XML provides secure-by-default JAXP factory creation, abstracting over implementation-specific XXE securing differences between + the stock JDK and external JAXP implementations.</p> + </section> + <section id="why"> + <h1> + <img src="org/apache/commons/xml/doc-files/leaf.svg" style="height: 1em; padding-right: 0.25em" alt="leaf">Why + </h1> + <p>Any Java library that parses XML has to secure JAXP before handing a factory to user code, and every library ends up copy-pasting the same securing + snippet. The snippet is fragile: the attributes and features needed to secure a factory are not standardized, each JAXP implementation exposes a slightly + different set, and setting an unknown one throws an exception that callers routinely swallow. Writing this block correctly for every implementation is + real work, and duplicating it across projects means every project owns the maintenance burden on its own.</p> + <p> + Defaults are also uneven. The stock JDK SAX and DOM parsers already prevent external entity resolution through + <code>FEATURE_SECURE_PROCESSING</code> + , and JAXP 1.5 conformant implementations ship reasonable defaults for most attacks. Others, such as standalone Xerces, Woodstox, or Saxon’s TrAX, need + further configuration before they reach the same baseline. A library author has no control over which implementation is on the classpath at runtime, so + the effective security posture of their code depends on a deployment decision made elsewhere. + </p> + <p> + This library provides that baseline. Each + <code>org.apache.commons.xml</code> + factory call returns a new factory secured by an implementation-specific recipe, so the returned object behaves the same way security-wise regardless of + which JAXP implementation resolved. Security becomes a property of the call, not of the classpath, and there is one place to update when a new securing + setting becomes available or a default changes. + </p> + </section> + <section id="usage"> + <h1> + <img src="org/apache/commons/xml/doc-files/leaf.svg" style="height: 1em; padding-right: 0.25em" alt="leaf">Usage + </h1> + <p> + To add the library to your build, see <a href="dependency-info.html">Maven Coordinates</a>. (Maven Coordinates)[dependency-info.html] + </p> + <p> + Every factory method in + <code>org.apache.commons.xml</code> + returns a new, secured factory. Pick the one that matches the API you already use; no other configuration is required. On secured factories an external + resource reference (DTD, entity, schema, stylesheet) is never fetched: it resolves to empty content, so the parse continues without it (see Configuration + below). + </p> + <section id="supported-runtimes"> + <h2>Supported Runtimes</h2> + <p>The library requires OpenJDK 8 or later (or a JDK distribution built from it), or Android API level 26 or later.</p> + <p> + The security guarantees are defined only on the OpenJDK family (see the <a href="threat_model.html">Threat Model</a>). No version of Android supports + <code>FEATURE_SECURE_PROCESSING</code> + (so states <a href="https://developer.android.com/reference/javax/xml/parsers/DocumentBuilderFactory#setFeature%28java.lang.String,%20boolean%29">Android’s + own documentation</a>), so the library secures the platform’s parsers as best-effort. Android’s + <code>XmlPullParser</code> + API is not supported: it is not a JAXP API. + </p> + </section> + <section id="supported-implementations"> + <h2>Supported Implementations</h2> + <p> + Out of the box the library recognizes the stock JDK JAXP implementations, Apache Xerces 2.x, Woodstox, and Saxon-HE. If a factory resolves to an + implementation not covered by any bundled securing recipe, every + <code>org.apache.commons.xml</code> + factory method throws + <code>IllegalStateException</code> + with a message naming the unsupported class. Adding support for a new JAXP implementation requires a code change to this library. + </p> + <p> + <strong>DOM Parsing</strong> via + <code>DocumentBuilderFactory</code> + : + </p> + <div class="sourceCode" id="cb1"> + <pre class="sourceCode java"> + <code class="sourceCode java"> +import org.w3c.dom.Document; +import org.apache.commons.xml.SecureDocumentBuilderFactory; + +Document doc = SecureDocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream); + </code> + </pre> + </div> + <p> + <strong>SAX Parsing</strong> via + <code>SAXParserFactory</code> + : + </p> + <div class="sourceCode" id="cb2"> + <pre class="sourceCode java"> + <code class="sourceCode java"> +import org.apache.commons.xml.SecureSAXParserFactory; + +SecureSAXParserFactory.newInstance().newSAXParser().parse(inputStream, myDefaultHandler); + </code> + </pre> + </div> + <p> + <strong>Streaming (StAX) Parsing</strong> via + <code>XMLInputFactory</code> + : + </p> + <div class="sourceCode" id="cb3"> + <pre class="sourceCode java"> + <code class="sourceCode java"> +import javax.xml.stream.XMLStreamReader; +import org.apache.commons.xml.SecureXMLInputFactory; + +XMLStreamReader reader = SecureXMLInputFactory.newInstance().createXMLStreamReader(inputStream); + </code> + </pre> + </div> + <p> + <strong>XSLT Transforms</strong> via + <code>TransformerFactory</code> + : + </p> + <div class="sourceCode" id="cb4"> + <pre class="sourceCode java"> + <code class="sourceCode java"> +import javax.xml.transform.stream.StreamSource; +import javax.xml.transform.stream.StreamResult; +import org.apache.commons.xml.SecureTransformerFactory; + +SecureTransformerFactory.newInstance() + .newTransformer(new StreamSource(stylesheet)) + .transform(new StreamSource(inputStream), new StreamResult(outputStream)); + </code> + </pre> + </div> + <p> + <strong>XPath Queries</strong> via + <code>XPathFactory</code> + : + </p> + <div class="sourceCode" id="cb5"> + <pre class="sourceCode java"> + <code class="sourceCode java"> +import javax.xml.xpath.XPathConstants; +import org.w3c.dom.NodeList; +import org.apache.commons.xml.SecureXPathFactory; + +NodeList hits = (NodeList) SecureXPathFactory.newInstance() + .newXPath() + .evaluate("//item", doc, XPathConstants.NODESET); + </code> + </pre> + </div> + <p> + <strong>W3C XML Schema Validation</strong> via + <code>SchemaFactory</code> + : + </p> + <div class="sourceCode" id="cb6"> + <pre class="sourceCode java"> + <code class="sourceCode java"> +import javax.xml.XMLConstants; +import javax.xml.transform.stream.StreamSource; +import org.apache.commons.xml.SecureSchemaFactory; + +SecureSchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI) + .newSchema(new StreamSource(xsdStream)) + .newValidator() + .validate(new StreamSource(inputStream)); + </code> + </pre> + </div> + </section> + <section id="wrappers-not-the-original-factories"> + <h2>Wrappers, not the Original Factories</h2> + <p>A returned factory is not necessarily an instance of the underlying implementation. It might be (and usually is) a wrapper around it, so it cannot + be cast to the implementation’s own class. Everything else about the implementation’s behavior is preserved: features, properties, and attributes + delegate to it, and only the security behavior is applied.</p> + <p>Preserved behavior includes the choice of internal parsers. Each TrAX, XPath, or schema implementation has its own way of instantiating them, and + the library respects it:</p> + <ul> + <li>Stock JDK factories use the JDK parsers by default, and expose the <code>jdk.xml.overrideDefaultParser</code> feature (and Java system property + of the same name) to switch to parsers instantiated through <code>ServiceLoader</code>. + </li> + <li>Saxon selects its parsers through its own configuration.</li> + </ul> + <p>Whichever parser is selected, it is secured.</p> + </section> + <section id="factory-methods"> + <h2>Factory Methods</h2> + <p> + Each factory class mirrors every static factory method its JAXP counterpart offers, so a secured factory is a drop-in replacement at any construction + site: the class-name/class-loader overloads and the StAX + <code>newFactory</code> + family (JDK 8), + <code>newDefaultInstance()</code> + (Java 9, <a href="https://bugs.openjdk.org/browse/JDK-8169778">JDK-8169778</a>), and the namespace-aware + <code>newNSInstance()</code> + family (Java 13, <a href="https://bugs.openjdk.org/browse/JDK-8223423">JDK-8223423</a>). + </p> + <p> + All of these methods work on every supported runtime, including Java 8: - The + <code>newNSInstance</code> + methods enable namespace awareness on their non-NS counterpart, the behavior the JAXP methods are specified to have. - The + <code>newDefaultInstance</code> + methods resolve the platform’s own + <code>newDefaultInstance</code> + at run time and use it wherever the runtime provides one — Java 9 or later, and the Android API levels that ship the method — falling back to + instantiating the JDK’s built-in implementation by class name on Java 8. + </p> + <p> + The + <code>newDefaultInstance</code> + methods are an opt-out of JAXP pluggability: they pin the platform’s built-in implementation instead of whatever a classpath lookup would resolve. That + suits a library with minimal XML requirements, which can parse with the well-known platform parser rather than delegate the choice of implementation to + the application developer. + </p> + </section> + <section id="stylesheets-and-schemas"> + <h2>Stylesheets and Schemas</h2> + <p> + The securing applies to documents parsed through the returned factory. Stylesheets given to + <code>TransformerFactory.newTransformer(Source)</code> + and schemas given to + <code>SchemaFactory.newSchema(Source)</code> + are read by a parser the implementation picks internally, and that parser may not be secured (Saxon’s TrAX is one such case, see Building below). Treat + stylesheets and schemas as trusted input, or pre-parse them through a secured + <code>org.apache.commons.xml</code> + parser and pass the result as a + <code>DOMSource</code> + or + <code>SAXSource</code> + . A stylesheet also chooses where the transform writes ( + <code>xsl:result-document</code> + ): the securing governs reads only, so restrict output destinations yourself when running an untrusted stylesheet (see the <a href="threat_model.html">Threat + Model</a>). + </p> + </section> + <section id="transformer-handlers-and-filters"> + <h2>Transformer Handlers and Filters</h2> + <p> + The + <code>SAXTransformerFactory</code> + extension methods, + <code>newTransformerHandler(...)</code> + , + <code>newTemplatesHandler()</code> + and + <code>newXMLFilter(...)</code> + , if reachable by casting the factory from + <code>SecureTransformerFactory.newInstance()</code> + , produce handlers, filters and + <code>Templates</code> + carrying the same securing as the standard entry points: runtime + <code>document()</code> + resolves to empty content, and a filter with no caller-set parent parses its input through a secured reader. The SAX events you feed into a handler, and + a parent reader you set on a filter, are your own configuration, like any caller-supplied parser. See the <a href="threat_model.html">Threat Model</a> + for the exact scope. + </p> + </section> + <section id="caching-and-thread-safety"> + <h2>Caching and Thread-Safety</h2> + <p> + There is no caching or pooling inside + <code>org.apache.commons.xml</code> + ; callers on a hot path are responsible for their own caching. The returned factories inherit the thread-safety properties of the underlying JAXP + implementation, which in practice means they are not thread-safe. Create a new factory per thread or synchronize externally. + </p> + </section> + </section> + <section id="configuration"> + <h1> + <img src="org/apache/commons/xml/doc-files/leaf.svg" style="height: 1em; padding-right: 0.25em" alt="leaf">Configuration + </h1> + <p>The secured factories need no configuration. When a document references an external resource (a DTD, an external entity, a schema, an XInclude + target, or an XSLT document), the securing layer resolves the reference to an empty stream: nothing is fetched, nothing leaks into the result, and the + parse continues wherever the implementation can proceed with empty content. This forgiving default accommodates documents that merely carry such + references without needing them.</p> + <p> + If your application should reject such documents instead of parsing them, tighten the factory yourself. The securing floor stays underneath whatever you + configure, so the tightening carries <strong>no security weight</strong> and can be as strict as the application needs: + </p> + <ul> + <li>Set a stricter feature on the factory, for example <code>http://apache.org/xml/features/disallow-doctype-decl</code> to reject every document + carrying a DOCTYPE, on implementations that support the feature. + </li> + <li>Install a resolver that throws. A caller-supplied <code>EntityResolver</code>, <code>XMLResolver</code>, <code>LSResourceResolver</code> or <code>URIResolver</code> + is consulted before the securing floor, so an allow-list and a deny-all are both one resolver away. + </li> + </ul> + <p> + As a temporary debugging measure, set the system property + <code>org.apache.commons.xml.throwOnUnresolved</code> + to + <code>true</code> + : every unresolved external reference is then rejected with the resolution hook’s exception, and the message names the denied resource. The property is + read at resolution time, so it can be toggled on a running application; treat it as a diagnostic switch, not as an application configuration. + </p> + </section> +</body> \ No newline at end of file diff --git a/src/site/markdown/index.md b/src/site/markdown/index.md index 6a762f1..ed23283 100644 --- a/src/site/markdown/index.md +++ b/src/site/markdown/index.md @@ -18,232 +18,12 @@ limitations under the License. # Apache Commons Secure XML Apache Commons Secure XML is part of the -[Apache Commons](https://commons.apache.org/index.html) project. - -Apache Commons Secure XML provides secure-by-default JAXP factory creation, +[Apache Commons](https://commons.apache.org/index.html) and provides secure-by-default JAXP factory creation, abstracting over implementation-specific XXE securing differences between the stock JDK and external JAXP implementations. +Full documentation is provided in: -## Why - -Any Java library that parses XML has to secure JAXP before handing a factory to user code, and every library ends up -copy-pasting the same securing snippet. The snippet is fragile: the attributes and features needed to secure a factory -are not standardized, each JAXP implementation exposes a slightly different set, and setting an unknown one throws an -exception that callers routinely swallow. Writing this block correctly for every implementation is real work, and -duplicating it across projects means every project owns the maintenance burden on its own. - -Defaults are also uneven. The stock JDK SAX and DOM parsers already prevent external entity resolution through -`FEATURE_SECURE_PROCESSING`, and JAXP 1.5 conformant implementations ship reasonable defaults for most attacks. Others, -such as standalone Xerces, Woodstox, or Saxon's TrAX, need further configuration before they reach the same baseline. A -library author has no control over which implementation is on the classpath at runtime, so the effective security -posture of their code depends on a deployment decision made elsewhere. - -This library provides that baseline. Each `org.apache.commons.xml` factory call returns a new factory secured by an -implementation-specific recipe, so the returned object behaves the same way security-wise regardless of which JAXP -implementation resolved. Security becomes a property of the call, not of the classpath, and there is one place to -update when a new securing setting becomes available or a default changes. - -## Usage - -To add the library to your build, see <a href="dependency-info.html">Maven Coordinates</a>. -(Maven Coordinates)[dependency-info.html] - -Every factory method in `org.apache.commons.xml` returns a new, secured factory. -Pick the one that matches the API you already use; -no other configuration is required. -On secured factories an external resource reference (DTD, entity, schema, stylesheet) is never fetched: -it resolves to empty content, -so the parse continues without it -(see Configuration below). - -### Supported Runtimes - -The library requires OpenJDK 8 or later (or a JDK distribution built from it), or Android API level 26 or later. - -The security guarantees are defined only on the OpenJDK family -(see the [Threat Model](threat_model.html)). -No version of Android supports `FEATURE_SECURE_PROCESSING` -(so states [Android's own documentation](https://developer.android.com/reference/javax/xml/parsers/DocumentBuilderFactory#setFeature%28java.lang.String,%20boolean%29)), -so the library secures the platform's parsers as best-effort. -Android's `XmlPullParser` API is not supported: -it is not a JAXP API. - -### Supported Implementations - -Out of the box the library recognizes the stock JDK JAXP implementations, Apache Xerces 2.x, Woodstox, and Saxon-HE. If -a factory resolves to an implementation not covered by any bundled securing recipe, every `org.apache.commons.xml` factory method throws -`IllegalStateException` with a message naming the unsupported class. Adding support for a new JAXP implementation -requires a code change to this library. - -**DOM Parsing** via `DocumentBuilderFactory`: - -```java -import org.w3c.dom.Document; -import org.apache.commons.xml.SecureDocumentBuilderFactory; - -Document doc = SecureDocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream); -``` - -**SAX Parsing** via `SAXParserFactory`: - -```java -import org.apache.commons.xml.SecureSAXParserFactory; - -SecureSAXParserFactory.newInstance().newSAXParser().parse(inputStream, myDefaultHandler); -``` - -**Streaming (StAX) Parsing** via `XMLInputFactory`: - -```java -import javax.xml.stream.XMLStreamReader; -import org.apache.commons.xml.SecureXMLInputFactory; - -XMLStreamReader reader = SecureXMLInputFactory.newInstance().createXMLStreamReader(inputStream); -``` - -**XSLT Transforms** via `TransformerFactory`: - -```java -import javax.xml.transform.stream.StreamSource; -import javax.xml.transform.stream.StreamResult; -import org.apache.commons.xml.SecureTransformerFactory; - -SecureTransformerFactory.newInstance() - .newTransformer(new StreamSource(stylesheet)) - .transform(new StreamSource(inputStream), new StreamResult(outputStream)); -``` - -**XPath Queries** via `XPathFactory`: - -```java -import javax.xml.xpath.XPathConstants; -import org.w3c.dom.NodeList; -import org.apache.commons.xml.SecureXPathFactory; - -NodeList hits = (NodeList) SecureXPathFactory.newInstance() - .newXPath() - .evaluate("//item", doc, XPathConstants.NODESET); -``` - -**W3C XML Schema Validation** via `SchemaFactory`: - -```java -import javax.xml.XMLConstants; -import javax.xml.transform.stream.StreamSource; -import org.apache.commons.xml.SecureSchemaFactory; - -SecureSchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI) - .newSchema(new StreamSource(xsdStream)) - .newValidator() - .validate(new StreamSource(inputStream)); -``` - -### Wrappers, not the Original Factories - -A returned factory is not necessarily an instance of the underlying implementation. -It might be (and usually is) a wrapper around it, -so it cannot be cast to the implementation's own class. -Everything else about the implementation's behavior is preserved: -features, properties, and attributes delegate to it, -and only the security behavior is applied. - -Preserved behavior includes the choice of internal parsers. -Each TrAX, XPath, or schema implementation has its own way of instantiating them, -and the library respects it: - -- Stock JDK factories use the JDK parsers by default, - and expose the `jdk.xml.overrideDefaultParser` feature - (and Java system property of the same name) - to switch to parsers instantiated through `ServiceLoader`. -- Saxon selects its parsers through its own configuration. - -Whichever parser is selected, it is secured. - -### Factory Methods - -Each factory class mirrors every static factory method its JAXP counterpart offers, -so a secured factory is a drop-in replacement at any construction site: -the class-name/class-loader overloads and the StAX `newFactory` family (JDK 8), -`newDefaultInstance()` (Java 9, [JDK-8169778](https://bugs.openjdk.org/browse/JDK-8169778)), -and the namespace-aware `newNSInstance()` family (Java 13, [JDK-8223423](https://bugs.openjdk.org/browse/JDK-8223423)). - -All of these methods work on every supported runtime, including Java 8: -- The `newNSInstance` methods enable namespace awareness on their non-NS counterpart, - the behavior the JAXP methods are specified to have. -- The `newDefaultInstance` methods resolve the platform's own `newDefaultInstance` at run time - and use it wherever the runtime provides one — - Java 9 or later, and the Android API levels that ship the method — - falling back to instantiating the JDK's built-in implementation by class name on Java 8. - -The `newDefaultInstance` methods are an opt-out of JAXP pluggability: -they pin the platform's built-in implementation -instead of whatever a classpath lookup would resolve. -That suits a library with minimal XML requirements, -which can parse with the well-known platform parser -rather than delegate the choice of implementation to the application developer. - -### Stylesheets and Schemas - -The securing applies to documents parsed through the returned factory. Stylesheets given to -`TransformerFactory.newTransformer(Source)` and schemas given to `SchemaFactory.newSchema(Source)` are read by a parser -the implementation picks internally, and that parser may not be secured (Saxon's TrAX is one such case, see Building -below). Treat stylesheets and schemas as trusted input, or pre-parse them through a secured `org.apache.commons.xml` parser and -pass the result as a `DOMSource` or `SAXSource`. -A stylesheet also chooses where the transform writes (`xsl:result-document`): -the securing governs reads only, -so restrict output destinations yourself when running an untrusted stylesheet -(see the [Threat Model](threat_model.html)). - -### Transformer Handlers and Filters - -The `SAXTransformerFactory` extension methods, `newTransformerHandler(...)`, `newTemplatesHandler()` and `newXMLFilter(...)`, -if reachable by casting the factory from `SecureTransformerFactory.newInstance()`, -produce handlers, filters and `Templates` carrying the same securing as the standard entry points: -runtime `document()` resolves to empty content, -and a filter with no caller-set parent parses its input through a secured reader. -The SAX events you feed into a handler, and a parent reader you set on a filter, -are your own configuration, like any caller-supplied parser. -See the [Threat Model](threat_model.html) for the exact scope. - -### Caching and Thread-Safety - -There is no caching or pooling inside `org.apache.commons.xml`; callers on a hot path are responsible for their own caching. The -returned factories inherit the thread-safety properties of the underlying JAXP implementation, which in practice means -they are not thread-safe. Create a new factory per thread or synchronize externally. - -## Configuration - -The secured factories need no configuration. -When a document references an external resource -(a DTD, an external entity, a schema, an XInclude target, or an XSLT document), -the securing layer resolves the reference to an empty stream: -nothing is fetched, -nothing leaks into the result, -and the parse continues wherever the implementation can proceed with empty content. -This forgiving default accommodates documents that merely carry such references without needing them. - -If your application should reject such documents instead of parsing them, -tighten the factory yourself. -The securing floor stays underneath whatever you configure, -so the tightening carries **no security weight** -and can be as strict as the application needs: - -- Set a stricter feature on the factory, - for example `http://apache.org/xml/features/disallow-doctype-decl` - to reject every document carrying a DOCTYPE, - on implementations that support the feature. -- Install a resolver that throws. - A caller-supplied `EntityResolver`, `XMLResolver`, `LSResourceResolver` or `URIResolver` - is consulted before the securing floor, - so an allow-list and a deny-all are both one resolver away. - -As a temporary debugging measure, -set the system property `org.apache.commons.xml.throwOnUnresolved` to `true`: -every unresolved external reference is then rejected with the resolution hook's exception, -and the message names the denied resource. -The property is read at resolution time, -so it can be toggled on a running application; -treat it as a diagnostic switch, -not as an application configuration. - +- [Javadoc Overview](apidocs/index.html), +- [Javadoc Package Summary](apidocs/org/apache/commons/xml/package-summary.html), and +- [Project Reports](project-reports.html).
