This is an automated email from the ASF dual-hosted git repository.

ppkarwasz pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/commons-xml.git


The following commit(s) were added to refs/heads/main by this push:
     new 86d6166  Refactor XmlFactories into their hardening target factories. 
(#53)
86d6166 is described below

commit 86d6166d64ec7c8758291509fd844ce13d8f569c
Author: Gary Gregory <[email protected]>
AuthorDate: Thu Aug 27 12:56:18 2026 -0400

    Refactor XmlFactories into their hardening target factories. (#53)
    
    Missing Java 8 methods will be added in a follow up PR.
---
 RELEASE-NOTES.txt                                  |   2 +-
 pom.xml                                            |   2 +-
 src/changes/changes.xml                            |   6 +-
 .../commons/xml/FallbackIgnoreEntityResolver2.java |   2 +-
 .../xml/HardeningDocumentBuilderFactory.java       |  34 +++-
 .../org/apache/commons/xml/HardeningException.java |  17 +-
 .../commons/xml/HardeningSAXParserFactory.java     |  31 ++-
 .../apache/commons/xml/HardeningSchemaFactory.java |  39 +++-
 .../commons/xml/HardeningTransformerFactory.java   |  36 +++-
 .../commons/xml/HardeningXMLInputFactory.java      |  21 +-
 .../apache/commons/xml/HardeningXPathFactory.java  |  23 ++-
 .../apache/commons/xml/TransformerHardener.java    |   2 +-
 .../java/org/apache/commons/xml/XmlFactories.java  | 213 ---------------------
 .../java/org/apache/commons/xml/package-info.java  |  47 ++++-
 src/site/markdown/index.md                         |  36 ++--
 src/site/markdown/threat_model.md                  |  22 +--
 .../commons/xml/AssociatedStylesheetTest.java      |   2 +-
 .../org/apache/commons/xml/AttackTestSupport.java  |  98 +++++-----
 .../org/apache/commons/xml/DenyUnresolvedTest.java |   6 +-
 .../commons/xml/EntityResolverFloorTest.java       |  22 +--
 ...sTest.java => HardeningFactoriesSmokeTest.java} |  48 +++--
 .../org/apache/commons/xml/ResetHardeningTest.java |   8 +-
 .../apache/commons/xml/SaxSurfaceTestSupport.java  |   4 +-
 .../commons/xml/SchemaFactoryLanguageTest.java     |   4 +-
 .../apache/commons/xml/SchemaLocationDomTest.java  |   2 +-
 .../commons/xml/SchemaLocationPropertyTest.java    |   4 +-
 .../apache/commons/xml/SchemaLocationSaxTest.java  |   2 +-
 .../apache/commons/xml/ShadingFootprintTest.java   |  16 +-
 .../java/org/apache/commons/xml/XIncludeTest.java  |  22 +--
 .../commons/xml/XMLFilterParseStringTest.java      |   2 +-
 .../apache/commons/xml/XPathInputSourceTest.java   |   6 +-
 31 files changed, 382 insertions(+), 397 deletions(-)

diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index 04922b1..16be4d5 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -29,7 +29,7 @@ First release.
 New features
 ------------
 
-*                  Secure-by-default JAXP factory creation via XmlFactories, 
with implementation-specific hardening recipes for the
+*                  Secure-by-default JAXP factory creation via 
org.apache.commons.xml, with implementation-specific hardening recipes for the
                    stock JDK, Android, Apache Xalan, Apache Xerces, Woodstox, 
and Saxon-HE.
 
 
diff --git a/pom.xml b/pom.xml
index db96027..d9fdfc7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -149,7 +149,7 @@ limitations under the License.
         <artifactId>maven-surefire-plugin</artifactId>
         <!--
           Make the underlying JAXP factories as permissive as the JDK allows. 
Hardened factories produced by
-          XmlFactories should still block every attack the test suite throws 
at them; if a test passes only
+          org.apache.commons.xml should still block every attack the test 
suite throws at them; if a test passes only
           because the JDK's defaults intervened, we want it to fail here so 
the gap shows up.
 
           Inherited by every execution defined below.
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index a7b57e8..09bbc39 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -28,14 +28,14 @@ The <action> type attribute can be add, update, fix, or 
remove.
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
           xsi:schemaLocation="http://maven.apache.org/changes/2.0.0 
https://maven.apache.org/xsd/changes-2.0.0.xsd";>
   <properties>
-    <title>Apache Commons XML - Changes</title>
+    <title>Apache Commons XML Changes</title>
   </properties>
   <body>
     <release version="1.0.0" date="YYYY-MM-DD" description="First release, 
requires Java 8 or above.">
       <!-- SPECIAL -->
-      <action type="add" dev="ggregory" due-to="Piotr P. Karwasz, Gary 
Gregory">This is the first release.</action>      
+      <action type="add" dev="ggregory" due-to="Piotr P. Karwasz, Gary 
Gregory">This is the first release of Apache Commons XML.</action>      
       <!-- ADD -->
-      <action type="add" dev="ppkarwasz" due-to="Piotr P. Karwasz, Gary 
Gregory">Add secure-by-default JAXP factory creation via XmlFactories, donated 
from the copernik-xml-factory project 
(https://github.com/copernik-eu/copernik-xml-factory) and covering the stock 
JDK, Android, Apache Xalan, Apache Xerces, Woodstox, and Saxon-HE.</action>
+      <action type="add" dev="ppkarwasz" due-to="Piotr P. Karwasz, Gary 
Gregory">Add secure-by-default JAXP factory creation via 
org.apache.commons.xml, donated from the copernik-xml-factory project 
(https://github.com/copernik-eu/copernik-xml-factory) and covering the stock 
JDK, Android, Apache Xalan, Apache Xerces, Woodstox, and Saxon-HE.</action>
       <action type="add" dev="ppkarwasz" due-to="Piotr P. Karwasz, Gary 
Gregory" issue="COMMONSXML-9">Install a non-removable resolver floor on every 
resolver channel (EntityResolver, LSResourceResolver, URIResolver, and 
XMLResolver), routing caller-supplied resolvers through it as 
allow-lists.</action>
       <action type="add" dev="ppkarwasz" due-to="Piotr P. Karwasz, Gary 
Gregory">Harden the SAXTransformerFactory extension surface 
(TransformerHandler, TemplatesHandler, and XMLFilter) and 
TransformerFactory.getAssociatedStylesheet.</action>
       <action type="add" dev="ppkarwasz" due-to="Piotr P. Karwasz, Jarek 
Potiuk, Gary Gregory">Document the threat model on the project site, including 
the denied-fetch contract and the supported runtime floor (OpenJDK 8 and 
Android API 33 or later).</action>
diff --git 
a/src/main/java/org/apache/commons/xml/FallbackIgnoreEntityResolver2.java 
b/src/main/java/org/apache/commons/xml/FallbackIgnoreEntityResolver2.java
index 0b7b774..bda79e2 100644
--- a/src/main/java/org/apache/commons/xml/FallbackIgnoreEntityResolver2.java
+++ b/src/main/java/org/apache/commons/xml/FallbackIgnoreEntityResolver2.java
@@ -109,7 +109,7 @@ final EntityResolver getDelegate() {
      * @param baseURI  The base URI for relative resolution, or {@code null}.
      * @param systemId The system identifier of the unresolved entity.
      * @return An empty {@link InputSource} carrying the requested identifiers.
-     * @throws SAXException when {@value XmlFactories#THROW_ON_UNRESOLVED} is 
set: unresolved references are rejected instead of resolved to empty.
+     * @throws SAXException when {@value 
HardeningException#THROW_ON_UNRESOLVED} is set: unresolved references are 
rejected instead of resolved to empty.
      * @throws IOException  never by the default implementation.
      */
     protected InputSource onUnresolved(final String name, final String 
publicId, final String baseURI, final String systemId) throws SAXException, 
IOException {
diff --git 
a/src/main/java/org/apache/commons/xml/HardeningDocumentBuilderFactory.java 
b/src/main/java/org/apache/commons/xml/HardeningDocumentBuilderFactory.java
index 19a6ec3..fc6aeb5 100644
--- a/src/main/java/org/apache/commons/xml/HardeningDocumentBuilderFactory.java
+++ b/src/main/java/org/apache/commons/xml/HardeningDocumentBuilderFactory.java
@@ -19,8 +19,10 @@
 
 import java.util.Objects;
 
+import javax.xml.XMLConstants;
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.FactoryConfigurationError;
 import javax.xml.parsers.ParserConfigurationException;
 import javax.xml.validation.Schema;
 
@@ -28,12 +30,36 @@
 
 /**
  * {@link DocumentBuilderFactory} wrapper that keeps an ignore-all {@link 
EntityResolver} floor on every {@link DocumentBuilder} produced.
- *
- * <p>Wraps each produced builder in a {@link HardeningDocumentBuilder}; 
required when the underlying factory carries no resolver of its own and does 
not honor
+ * <p>
+ * Wraps each produced builder in a {@link HardeningDocumentBuilder}; required 
when the underlying factory carries no resolver of its own and does not honor
  * JAXP 1.5 {@code ACCESS_EXTERNAL_*} (e.g. the external Xerces distribution). 
A caller-set resolver is routed through the floor rather than replacing it. Kept
- * as a standalone wrapper so any hardener can reuse the floor.</p>
+ * as a standalone wrapper so any hardener can reuse the floor.
+ * </p>
+ *
+ * @see org.apache.commons.xml
  */
-final class HardeningDocumentBuilderFactory extends DocumentBuilderFactory {
+public final class HardeningDocumentBuilderFactory extends 
DocumentBuilderFactory {
+
+    /**
+     * Returns a new, hardened {@link DocumentBuilderFactory}.
+     * <p>
+     * Beyond the three universal guarantees on {@link 
org.apache.commons.xml}, XInclude resolution is denied by default. When
+     * {@link DocumentBuilderFactory#setXIncludeAware(boolean) 
setXIncludeAware(true)} is called on the returned factory, the parser will 
process
+     * {@code xi:include} elements but every external resource lookup is 
rejected. To permit specific trusted resources, install an
+     * {@link org.xml.sax.EntityResolver EntityResolver} on the {@link 
DocumentBuilder} that allow-lists them; any href the resolver does not 
explicitly allow
+     * stays blocked.
+     * </p>
+     *
+     * @return A hardened factory.
+     * @throws IllegalStateException     Thrown if a required hardening 
setting cannot be applied to the underlying implementation.
+     * @throws IllegalStateException     Thrown if a (non-Andoid) factory 
cannot support the secure processing feature
+     *                                   {@link 
XMLConstants#FEATURE_SECURE_PROCESSING}.
+     * @throws FactoryConfigurationError Thrown from a factory in case of a 
{@link java.util.ServiceConfigurationError service configuration error} or if 
the
+     *                                   implementation is not available or 
cannot be instantiated.
+     */
+    public static DocumentBuilderFactory newInstance() {
+        return 
DocumentBuilderHardener.harden(DocumentBuilderFactory.newInstance());
+    }
 
     private final DocumentBuilderFactory delegate;
 
diff --git a/src/main/java/org/apache/commons/xml/HardeningException.java 
b/src/main/java/org/apache/commons/xml/HardeningException.java
index 4dd123c..163ead0 100644
--- a/src/main/java/org/apache/commons/xml/HardeningException.java
+++ b/src/main/java/org/apache/commons/xml/HardeningException.java
@@ -47,7 +47,7 @@ final class HardeningException extends IllegalStateException {
      */
     static String forbidden(final String type, final String namespace, final 
String publicId, final String systemId, final String baseURI) {
         return String.format("External resource fetch forbidden by %s: 
type=%s, namespace=%s, publicId=%s, systemId=%s, baseURI=%s",
-                XmlFactories.THROW_ON_UNRESOLVED, type, namespace, publicId, 
systemId, baseURI);
+                HardeningException.THROW_ON_UNRESOLVED, type, namespace, 
publicId, systemId, baseURI);
     }
 
     /**
@@ -66,14 +66,23 @@ static HardeningException settingFailed(final String kind, 
final String name, fi
     /**
      * Whether unresolved external references must be rejected instead of 
resolved to empty content.
      *
-     * <p>Read per resolution, so the {@value 
XmlFactories#THROW_ON_UNRESOLVED} system property also toggles factories that 
already exist.</p>
+     * <p>Read per resolution, so the {@value 
HardeningException#THROW_ON_UNRESOLVED} system property also toggles factories 
that already exist.</p>
      *
-     * @return {@code true} when the {@value XmlFactories#THROW_ON_UNRESOLVED} 
system property is set.
+     * @return {@code true} when the {@value 
HardeningException#THROW_ON_UNRESOLVED} system property is set.
      */
     static boolean throwOnUnresolved() {
-        return Boolean.getBoolean(XmlFactories.THROW_ON_UNRESOLVED);
+        return Boolean.getBoolean(HardeningException.THROW_ON_UNRESOLVED);
     }
 
+    /**
+     * System property that switches unresolved external references from the 
default empty resolution to a thrown exception.
+     * <p>
+     * How to enable: set {@code 
-Dorg.apache.commons.xml.throwOnUnresolved=true}. The property is read at 
resolution time, so it also applies to factories
+     * created before it was set; references resolved by a caller-supplied 
resolver are unaffected.
+     * </p>
+     */
+    static final String THROW_ON_UNRESOLVED = 
"org.apache.commons.xml.throwOnUnresolved";
+
     HardeningException(final String message, final Throwable cause) {
         super(message, cause);
     }
diff --git 
a/src/main/java/org/apache/commons/xml/HardeningSAXParserFactory.java 
b/src/main/java/org/apache/commons/xml/HardeningSAXParserFactory.java
index 0454f15..abc3521 100644
--- a/src/main/java/org/apache/commons/xml/HardeningSAXParserFactory.java
+++ b/src/main/java/org/apache/commons/xml/HardeningSAXParserFactory.java
@@ -19,6 +19,7 @@
 
 import java.util.Objects;
 
+import javax.xml.parsers.FactoryConfigurationError;
 import javax.xml.parsers.ParserConfigurationException;
 import javax.xml.parsers.SAXParser;
 import javax.xml.parsers.SAXParserFactory;
@@ -31,12 +32,33 @@
 
 /**
  * Universal SAX factory wrapper that funnels every produced parser through 
{@link SAXParserHardener#hardenReader(XMLReader)}.
+ * <p>
+ * {@link SAXParserFactory} exposes only a feature API and no property API, so 
the per-parse hardening (limits, entity blocking, implementation-specific 
fixups)
+ * has to run on each {@link XMLReader} the factory produces. This wrapper 
returns a {@link HardeningSAXParser}, which applies that hardening lazily to 
both the
+ * SAX 2 {@link XMLReader} and the SAX 1 {@link org.xml.sax.Parser} it exposes.
+ * </p>
  *
- * <p>{@link SAXParserFactory} exposes only a feature API and no property API, 
so the per-parse hardening (limits, entity blocking, implementation-specific
- * fixups) has to run on each {@link XMLReader} the factory produces. This 
wrapper returns a {@link HardeningSAXParser}, which applies that hardening 
lazily to
- * both the SAX 2 {@link XMLReader} and the SAX 1 {@link org.xml.sax.Parser} 
it exposes.</p>
+ * @see org.apache.commons.xml
  */
-final class HardeningSAXParserFactory extends SAXParserFactory {
+public final class HardeningSAXParserFactory extends SAXParserFactory {
+
+    /**
+     * Returns a new, hardened {@link SAXParserFactory}.
+     * <p>
+     * Beyond the three universal guarantees on {@link 
org.apache.commons.xml}, XInclude resolution is denied by default. When
+     * {@link SAXParserFactory#setXIncludeAware(boolean) 
setXIncludeAware(true)} is called on the returned factory, the parser will 
process {@code xi:include}
+     * elements but every external resource lookup is rejected. To permit 
specific trusted resources, install an {@link org.xml.sax.EntityResolver
+     * EntityResolver} on the {@link org.xml.sax.XMLReader} that allow-lists 
them; any href the resolver does not explicitly allow stays blocked.
+     * </p>
+     *
+     * @return A hardened factory.
+     * @throws IllegalStateException     Thrown if a required hardening 
setting cannot be applied to the underlying implementation.
+     * @throws FactoryConfigurationError Thrown from {@link SAXParserFactory} 
in case of a {@link java.util.ServiceConfigurationError service configuration
+     *                                   error} or if the implementation is 
not available or cannot be instantiated.
+     */
+    public static SAXParserFactory newInstance() {
+        return SAXParserHardener.harden(SAXParserFactory.newInstance());
+    }
 
     private final SAXParserFactory delegate;
 
@@ -104,5 +126,4 @@ public void setValidating(final boolean validating) {
     public void setXIncludeAware(final boolean state) {
         delegate.setXIncludeAware(state);
     }
-
 }
diff --git a/src/main/java/org/apache/commons/xml/HardeningSchemaFactory.java 
b/src/main/java/org/apache/commons/xml/HardeningSchemaFactory.java
index c55fe91..48c3c1a 100644
--- a/src/main/java/org/apache/commons/xml/HardeningSchemaFactory.java
+++ b/src/main/java/org/apache/commons/xml/HardeningSchemaFactory.java
@@ -24,6 +24,7 @@
 import javax.xml.transform.TransformerConfigurationException;
 import javax.xml.validation.Schema;
 import javax.xml.validation.SchemaFactory;
+import javax.xml.validation.SchemaFactoryConfigurationError;
 import javax.xml.validation.Validator;
 
 import org.w3c.dom.ls.LSResourceResolver;
@@ -34,7 +35,7 @@
 
 /**
  * Capability-driven hardening wrapper for any {@link SchemaFactory} on the 
classpath, the same recipe for every implementation. It is the entry point 
reached
- * by {@link XmlFactories#newSchemaFactory(String)}; there is no 
per-implementation branching, no {@code FEATURE_SECURE_PROCESSING} and no limit 
configuration on the
+ * by {@link HardeningSchemaFactory#newInstance(String)}; there is no 
per-implementation branching, no {@code FEATURE_SECURE_PROCESSING} and no limit 
configuration on the
  * factory itself.
  *
  * <p>Three layers cooperate:</p>
@@ -47,14 +48,18 @@
  *   <li>{@link HardeningValidator} rewrites the Source on every {@link 
Validator#validate(Source)} call.</li>
  * </ol>
  *
- * <p>The hardened reader supplied by {@link 
SAXParserHardener#hardenSource(Source)} already carries {@code 
FEATURE_SECURE_PROCESSING} and the processing limits, so a
+ * <p>
+ * The hardened reader supplied by {@link 
SAXParserHardener#hardenSource(Source)} already carries {@code 
FEATURE_SECURE_PROCESSING} and the processing limits, so a
  * DOCTYPE, external entity or Billion Laughs payload in the schema or 
instance document is bounded there rather than on this factory. The JAXP 1.5
  * {@code ACCESS_EXTERNAL_*} properties are deliberately not set: the resolver 
floor already blocks the same fetches on every implementation, and the JDK 8
  * {@code SchemaFactory} has a bug whereby those properties keep blocking even 
when a caller's own resolver would grant the access. The floor is a 
non-removable
  * lower bound: a caller-set {@link LSResourceResolver} is routed through it 
(opting a specific lookup in by returning a non-{@code null} result) rather than
- * replacing it, so hardening cannot be dropped by swapping the resolver.</p>
+ * replacing it, so hardening cannot be dropped by swapping the resolver.
+ * </p>
+ *
+ * @see org.apache.commons.xml
  */
-final class HardeningSchemaFactory extends SchemaFactory {
+public final class HardeningSchemaFactory extends SchemaFactory {
 
     /**
      * Hardens every schema source through {@link 
SAXParserHardener#hardenSource(Source)}.
@@ -77,6 +82,30 @@ private static Source[] harden(final Source[] schemas) 
throws SAXException {
         return hardened;
     }
 
+    /**
+     * Returns a new, hardened {@link SchemaFactory} for the given schema 
language.
+     * <p>
+     * Beyond the three universal guarantees on {@link org.apache.commons.xml}:
+     * </p>
+     * <ul>
+     * <li>{@code xs:import}, {@code xs:include} and {@code xs:redefine} 
schemaLocation URIs are not resolved during schema compilation, and</li>
+     * <li>{@code xsi:schemaLocation} / {@code xsi:noNamespaceSchemaLocation} 
hints in instance documents are not resolved during validation.</li>
+     * </ul>
+     * <p>
+     * The same guarantees apply to {@link javax.xml.validation.Validator} and 
{@link javax.xml.validation.ValidatorHandler} instances produced from the
+     * resulting {@link javax.xml.validation.Schema}.
+     * </p>
+     *
+     * @param schemaLanguage The schema language, as accepted by {@link 
SchemaFactory#newInstance(String)}.
+     * @return A hardened factory.
+     * @throws IllegalArgumentException        Thrown if no implementation of 
the schema language is available.
+     * @throws NullPointerException            Thrown if {@code 
schemaLanguage} is {@code null}.
+     * @throws SchemaFactoryConfigurationError Thrown if a configuration error 
is encountered.
+     */
+    public static SchemaFactory newInstance(final String schemaLanguage) {
+        return new 
HardeningSchemaFactory(SchemaFactory.newInstance(schemaLanguage));
+    }
+
     private final SchemaFactory delegate;
 
     private final FallbackIgnoreLSResourceResolver floor = new 
FallbackIgnoreLSResourceResolver(null);
@@ -144,12 +173,12 @@ public void setFeature(final String name, final boolean 
value) throws SAXNotReco
         delegate.setFeature(name, value);
     }
 
+
     @Override
     public void setProperty(final String name, final Object object) throws 
SAXNotRecognizedException, SAXNotSupportedException {
         delegate.setProperty(name, object);
     }
 
-
     @Override
     public void setResourceResolver(final LSResourceResolver resourceResolver) 
{
         // Route a caller resolver through the floor instead of replacing it, 
so the ignore-all lower bound cannot be removed.
diff --git 
a/src/main/java/org/apache/commons/xml/HardeningTransformerFactory.java 
b/src/main/java/org/apache/commons/xml/HardeningTransformerFactory.java
index 948c5b9..b64bcfc 100644
--- a/src/main/java/org/apache/commons/xml/HardeningTransformerFactory.java
+++ b/src/main/java/org/apache/commons/xml/HardeningTransformerFactory.java
@@ -30,6 +30,7 @@
 import javax.xml.transform.Templates;
 import javax.xml.transform.Transformer;
 import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerFactory;
 import javax.xml.transform.URIResolver;
 import javax.xml.transform.dom.DOMSource;
 import javax.xml.transform.sax.SAXSource;
@@ -50,7 +51,7 @@
  *
  * <p>Used by providers whose underlying TrAX implementation pulls a new 
{@code SAXParserFactory.newInstance()} for any Source that is not already a
  * {@link SAXSource} carrying its own {@link XMLReader}, and only sets {@link 
javax.xml.XMLConstants#FEATURE_SECURE_PROCESSING FSP} on the resulting reader.
- * Wrapping the factory and rewriting the Source upstream guarantees the parse 
runs through an {@link XmlFactories}-hardened reader instead.</p>
+ * Wrapping the factory and rewriting the Source upstream guarantees the parse 
runs through an {@link org.apache.commons.xml}-hardened reader instead.</p>
  *
  * <p>Three layers cooperate:</p>
  * <ol>
@@ -69,11 +70,13 @@
  * <h2>Caveats</h2>
  * <ul>
  *   <li>A {@link SAXSource} that carries its own {@link XMLReader} is trusted 
as-is: the caller is expected to supply a hardened reader (via
- *       {@link XmlFactories#newSAXParserFactory()}) in that case. The same 
applies to the SAX events a caller feeds into a handler, and to a parent reader 
a
+ *       {@link HardeningSAXParserFactory#newInstance()}) in that case. The 
same applies to the SAX events a caller feeds into a handler, and to a parent 
reader a
  *       caller sets on a returned {@link XMLFilter}.</li>
  * </ul>
+ *
+ * @see org.apache.commons.xml
  */
-final class HardeningTransformerFactory extends SAXTransformerFactory {
+public final class HardeningTransformerFactory extends SAXTransformerFactory {
 
     /**
      * Parses a reader-less source into a DOM through a hardened, 
namespace-aware {@link javax.xml.parsers.DocumentBuilder} and returns a {@link 
DOMSource}
@@ -288,4 +291,31 @@ public void setFeature(final String name, final boolean 
value) throws Transforme
     public void setURIResolver(final URIResolver resolver) {
         floor.setDelegate(resolver);
     }
+
+    /**
+     * Returns a new, hardened {@link TransformerFactory}.
+     * <p>
+     * Beyond the three universal guarantees on {@link 
org.apache.commons.xml}: {@code xsl:import}, {@code xsl:include} and {@code 
document()} URIs are not resolved.
+     * </p>
+     * <p>
+     * The guarantees govern what the transform reads, not what it writes: an 
output instruction like {@code xsl:result-document} still writes wherever the
+     * stylesheet directs, so an untrusted stylesheet's output destinations 
must be restricted outside the library.
+     * </p>
+     * <p>
+     * The guarantees apply to every parser the factory creates internally for 
the standard {@link TransformerFactory} entry points: stylesheet compilation
+     * ({@link TransformerFactory#newTemplates(javax.xml.transform.Source) 
newTemplates(Source)},
+     * {@link TransformerFactory#newTransformer(javax.xml.transform.Source) 
newTransformer(Source)}) and source-document reading at
+     * {@code Transformer.transform(Source, Result)} time.
+     * </p>
+     * <p>
+     * The {@link javax.xml.transform.sax.SAXTransformerFactory} extension 
methods ({@code newTransformerHandler(..)}, {@code newTemplatesHandler()},
+     * {@code newXMLFilter(..)}), if reachable by casting the returned 
factory, produce objects carrying the same guarantees.
+     * </p>
+     *
+     * @return A hardened factory.
+     * @throws IllegalStateException if a required hardening setting cannot be 
applied to the underlying implementation.
+     */
+    public static TransformerFactory newInstance() {
+        return TransformerHardener.harden(TransformerFactory.newInstance());
+    }
 }
diff --git a/src/main/java/org/apache/commons/xml/HardeningXMLInputFactory.java 
b/src/main/java/org/apache/commons/xml/HardeningXMLInputFactory.java
index a0635b6..63d21d8 100644
--- a/src/main/java/org/apache/commons/xml/HardeningXMLInputFactory.java
+++ b/src/main/java/org/apache/commons/xml/HardeningXMLInputFactory.java
@@ -21,6 +21,7 @@
 import java.io.Reader;
 import java.util.Objects;
 
+import javax.xml.parsers.FactoryConfigurationError;
 import javax.xml.stream.EventFilter;
 import javax.xml.stream.StreamFilter;
 import javax.xml.stream.XMLEventReader;
@@ -49,8 +50,10 @@
  * wrapped in a new floor. This matters because Woodstox does not chain 
resolvers: when a resolver returns {@code null}, {@code DefaultInputResolver} 
falls
  * through to fetching the systemId URL itself, so a caller-set resolver that 
returns {@code null} must still land behind the floor. {@link 
#getXMLResolver()} and
  * {@code getProperty} report the caller's resolver unwrapped.</p>
+ *
+ * @see org.apache.commons.xml
  */
-final class HardeningXMLInputFactory extends XMLInputFactory {
+public final class HardeningXMLInputFactory extends XMLInputFactory {
 
     private static boolean isResolverProperty(final String name) {
         return XMLInputFactory.RESOLVER.equals(name)
@@ -59,6 +62,20 @@ private static boolean isResolverProperty(final String name) 
{
                 || StaxHardener.WSTX_UNDECLARED_ENTITY_RESOLVER.equals(name);
     }
 
+    /**
+     * Returns a new, hardened {@link XMLInputFactory}.
+     * <p>
+     * The three universal guarantees on {@link org.apache.commons.xml} apply; 
StAX exposes no additional vectors beyond them.
+     * </p>
+     *
+     * @return A hardened factory.
+     * @throws IllegalStateException     Thrown if a required hardening 
setting cannot be applied to the underlying implementation.
+     * @throws FactoryConfigurationError Thrown if an instance of this factory 
cannot be loaded.
+     */
+    public static XMLInputFactory newInstance() {
+        return StaxHardener.harden(XMLInputFactory.newInstance());
+    }
+
     private static XMLResolver unwrap(final XMLResolver resolver) {
         return resolver instanceof FallbackIgnoreXMLResolver ? 
((FallbackIgnoreXMLResolver) resolver).getDelegate() : resolver;
     }
@@ -151,12 +168,12 @@ public XMLStreamReader createXMLStreamReader(final String 
systemId, final Reader
         return delegate.createXMLStreamReader(systemId, reader);
     }
 
+
     @Override
     public XMLEventAllocator getEventAllocator() {
         return delegate.getEventAllocator();
     }
 
-
     @Override
     public Object getProperty(final String name) {
         if (isResolverProperty(name)) {
diff --git a/src/main/java/org/apache/commons/xml/HardeningXPathFactory.java 
b/src/main/java/org/apache/commons/xml/HardeningXPathFactory.java
index 6668057..feccab9 100644
--- a/src/main/java/org/apache/commons/xml/HardeningXPathFactory.java
+++ b/src/main/java/org/apache/commons/xml/HardeningXPathFactory.java
@@ -31,8 +31,10 @@
  * <p>Required because {@link 
javax.xml.XMLConstants#FEATURE_SECURE_PROCESSING} on the factory governs only 
the XPath engine: the stock JDK and Apache Xalan
  * implement the {@link org.xml.sax.InputSource}-taking {@code evaluate} entry 
points by provisioning an internal document parser the feature does not reach.
  * The wrapper performs that document build itself through a hardened parser 
instead; see {@link HardeningXPath}.</p>
+ *
+ * @see org.apache.commons.xml
  */
-final class HardeningXPathFactory extends XPathFactory {
+public final class HardeningXPathFactory extends XPathFactory {
 
     private final XPathFactory delegate;
 
@@ -76,4 +78,23 @@ public void setXPathFunctionResolver(final 
XPathFunctionResolver resolver) {
     public void setXPathVariableResolver(final XPathVariableResolver resolver) 
{
         delegate.setXPathVariableResolver(resolver);
     }
+
+    /**
+     * Returns a new, hardened {@link XPathFactory} for the default XPath 
object model.
+     * <p>
+     * Beyond the three universal guarantees on {@link 
org.apache.commons.xml}, URI-fetching XPath 3.1+ functions ({@code doc()}, 
{@code collection()},
+     * {@code unparsed-text()}) are not resolved.
+     * </p>
+     * <p>
+     * The guarantees also cover the document parse behind {@code 
XPath.evaluate(String, InputSource)} and {@code 
XPathExpression.evaluate(InputSource)}: the
+     * input document is built through a hardened, namespace-aware {@link 
javax.xml.parsers.DocumentBuilder} instead of the engine's internal parser.
+     * </p>
+     *
+     * @return A hardened factory.
+     * @throws IllegalStateException Thrown if a required hardening setting 
cannot be applied to the underlying implementation.
+     * @throws RuntimeException      Thrown if there is a failure in creating 
an {@link XPathFactory} for the default object model.
+     */
+    public static XPathFactory newXPathFactory() {
+        return XPathHardener.harden(XPathFactory.newInstance());
+    }
 }
diff --git a/src/main/java/org/apache/commons/xml/TransformerHardener.java 
b/src/main/java/org/apache/commons/xml/TransformerHardener.java
index 5b937b5..9945c24 100644
--- a/src/main/java/org/apache/commons/xml/TransformerHardener.java
+++ b/src/main/java/org/apache/commons/xml/TransformerHardener.java
@@ -41,7 +41,7 @@
  *         routed through the floor rather than replacing it, so a caller can 
opt a specific URI in but cannot reopen the fetch.</li>
  *     <li><strong>{@link HardeningTransformerFactory}</strong>: required. 
Both implementations fall back to {@code SAXParserFactory.newInstance()} to 
parse a
  *         stylesheet or source document that does not carry its own reader, 
and only set FSP on it; wrapping the factory rewrites every {@link Source} 
through an
- *         {@link XmlFactories}-hardened reader instead.</li>
+ *         {@link org.apache.commons.xml}-hardened reader instead.</li>
  * </ul>
  */
 final class TransformerHardener {
diff --git a/src/main/java/org/apache/commons/xml/XmlFactories.java 
b/src/main/java/org/apache/commons/xml/XmlFactories.java
deleted file mode 100644
index 0330be0..0000000
--- a/src/main/java/org/apache/commons/xml/XmlFactories.java
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * 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;
-
-import javax.xml.XMLConstants;
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.FactoryConfigurationError;
-import javax.xml.parsers.SAXParserFactory;
-import javax.xml.stream.XMLInputFactory;
-import javax.xml.transform.TransformerFactory;
-import javax.xml.validation.SchemaFactory;
-import javax.xml.validation.SchemaFactoryConfigurationError;
-import javax.xml.xpath.XPathFactory;
-
-/**
- * Entry point for obtaining hardened JAXP factories.
- *
- * <p>Every method on this class returns a <em>new, hardened</em> factory 
instance. No caching or pooling is performed; callers on a hot path are 
responsible
- * for their own caching.</p>
- *
- * <h2>Hardening guarantees</h2>
- *
- * <p>Every factory returned by this class makes the same three guarantees, 
regardless of which JAXP implementation is on the classpath:</p>
- *
- * <ul>
- *   <li><strong>External DTDs are not fetched.</strong></li>
- *   <li><strong>External entities are not resolved.</strong></li>
- *   <li><strong>Internal entity expansion is bounded</strong> by the 
platform's secure-processing limit, so DoS payloads such as Billion Laughs are 
rejected
- *       before they exhaust resources.</li>
- * </ul>
- *
- * <p>These guarantees are defined on OpenJDK 8 or later (and JDK 
distributions built from it). No version of Android supports
- * {@link javax.xml.XMLConstants#FEATURE_SECURE_PROCESSING}, so on Android 
(API level 19 or later) the hardening is applied as best-effort without a 
guarantee,
- * tested as complete starting with API level 33; see the threat model's 
"Assumptions about the environment".</p>
- *
- * <p>The guarantees hold whether or not the caller opts into DTD validation
- * ({@link javax.xml.parsers.DocumentBuilderFactory#setValidating(boolean) 
setValidating(true)}) or attaches a compiled XSD via
- * {@link 
javax.xml.parsers.DocumentBuilderFactory#setSchema(javax.xml.validation.Schema) 
setSchema}: every external resource the validation would otherwise
- * fetch (the DTD itself, an {@code xsi:schemaLocation} hint, an external 
entity referenced from the DTD) remains blocked.</p>
- *
- * <p>Each method on this class adds factory-specific guarantees on top of the 
three above, documented on the corresponding {@code newXxxFactory()} method.</p>
- *
- * <p>An unresolved external reference resolves to empty content by default, 
so the parse continues without the resource. To reject it with an exception
- * instead, set the system property {@code 
org.apache.commons.xml.throwOnUnresolved} to {@code true}; the property is read 
at resolution time, and references
- * resolved by a caller-supplied resolver are unaffected.</p>
- *
- * <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 hardening 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>The returned factories inherit the thread-safety properties of the 
underlying JAXP implementation, which in practice means they are <strong>not
- * guaranteed to be thread-safe</strong>. Create a new factory per thread or 
synchronize externally.</p>
- *
- * <p>This class itself is thread-safe: all methods are static and 
stateless.</p>
- */
-public final class XmlFactories {
-
-    /**
-     * System property that switches unresolved external references from the 
default empty resolution to a thrown exception.
-     * <p>
-     * How to enable: set {@code 
-Dorg.apache.commons.xml.throwOnUnresolved=true}. The property is read at 
resolution time, so it also applies to factories
-     * created before it was set; references resolved by a caller-supplied 
resolver are unaffected.
-     * </p>
-     */
-    static final String THROW_ON_UNRESOLVED = 
"org.apache.commons.xml.throwOnUnresolved";
-
-    /**
-     * Returns a new, hardened {@link DocumentBuilderFactory}.
-     * <p>
-     * Beyond the three universal guarantees on {@link XmlFactories}, XInclude 
resolution is denied by default. When
-     * {@link DocumentBuilderFactory#setXIncludeAware(boolean) 
setXIncludeAware(true)} is called on the returned factory, the parser will 
process
-     * {@code xi:include} elements but every external resource lookup is 
rejected. To permit specific trusted resources, install an
-     * {@link org.xml.sax.EntityResolver EntityResolver} on the {@link 
DocumentBuilder} that allow-lists them; any href the resolver does not 
explicitly allow
-     * stays blocked.
-     * </p>
-     *
-     * @return A hardened factory.
-     * @throws IllegalStateException     Thrown if a required hardening 
setting cannot be applied to the underlying implementation.
-     * @throws IllegalStateException     Thrown if a (non-Andoid) factory 
cannot support the secure processing feature
-     *                                   {@link 
XMLConstants#FEATURE_SECURE_PROCESSING}.
-     * @throws FactoryConfigurationError Thrown from a factory in case of a 
{@link java.util.ServiceConfigurationError service configuration error} or if 
the
-     *                                   implementation is not available or 
cannot be instantiated.
-     */
-    public static DocumentBuilderFactory newDocumentBuilderFactory() {
-        return 
DocumentBuilderHardener.harden(DocumentBuilderFactory.newInstance());
-    }
-
-    /**
-     * Returns a new, hardened {@link SAXParserFactory}.
-     * <p>
-     * Beyond the three universal guarantees on {@link XmlFactories}, XInclude 
resolution is denied by default. When
-     * {@link SAXParserFactory#setXIncludeAware(boolean) 
setXIncludeAware(true)} is called on the returned factory, the parser will 
process {@code xi:include}
-     * elements but every external resource lookup is rejected. To permit 
specific trusted resources, install an {@link org.xml.sax.EntityResolver
-     * EntityResolver} on the {@link org.xml.sax.XMLReader} that allow-lists 
them; any href the resolver does not explicitly allow stays blocked.
-     * </p>
-     *
-     * @return A hardened factory.
-     * @throws IllegalStateException     Thrown if a required hardening 
setting cannot be applied to the underlying implementation.
-     * @throws FactoryConfigurationError Thrown from {@link SAXParserFactory} 
in case of a {@link java.util.ServiceConfigurationError service configuration
-     *                                   error} or if the implementation is 
not available or cannot be instantiated.
-     */
-    public static SAXParserFactory newSAXParserFactory() {
-        return SAXParserHardener.harden(SAXParserFactory.newInstance());
-    }
-
-    /**
-     * Returns a new, hardened {@link SchemaFactory} for the given schema 
language.
-     * <p>
-     * Beyond the three universal guarantees on {@link XmlFactories}:
-     * </p>
-     * <ul>
-     * <li>{@code xs:import}, {@code xs:include} and {@code xs:redefine} 
schemaLocation URIs are not resolved during schema compilation, and</li>
-     * <li>{@code xsi:schemaLocation} / {@code xsi:noNamespaceSchemaLocation} 
hints in instance documents are not resolved during validation.</li>
-     * </ul>
-     * <p>
-     * The same guarantees apply to {@link javax.xml.validation.Validator} and 
{@link javax.xml.validation.ValidatorHandler} instances produced from the
-     * resulting {@link javax.xml.validation.Schema}.
-     * </p>
-     *
-     * @param schemaLanguage The schema language, as accepted by {@link 
SchemaFactory#newInstance(String)}.
-     * @return A hardened factory.
-     * @throws IllegalArgumentException        Thrown if no implementation of 
the schema language is available.
-     * @throws NullPointerException            Thrown if {@code 
schemaLanguage} is {@code null}.
-     * @throws SchemaFactoryConfigurationError Thrown if a configuration error 
is encountered.
-     */
-    public static SchemaFactory newSchemaFactory(final String schemaLanguage) {
-        return new 
HardeningSchemaFactory(SchemaFactory.newInstance(schemaLanguage));
-    }
-
-    /**
-     * Returns a new, hardened {@link TransformerFactory}.
-     * <p>
-     * Beyond the three universal guarantees on {@link XmlFactories}: {@code 
xsl:import}, {@code xsl:include} and {@code document()} URIs are not resolved.
-     * </p>
-     * <p>
-     * The guarantees govern what the transform reads, not what it writes: an 
output instruction like {@code xsl:result-document} still writes wherever the
-     * stylesheet directs, so an untrusted stylesheet's output destinations 
must be restricted outside the library.
-     * </p>
-     * <p>
-     * The guarantees apply to every parser the factory creates internally for 
the standard {@link TransformerFactory} entry points: stylesheet compilation
-     * ({@link TransformerFactory#newTemplates(javax.xml.transform.Source) 
newTemplates(Source)},
-     * {@link TransformerFactory#newTransformer(javax.xml.transform.Source) 
newTransformer(Source)}) and source-document reading at
-     * {@code Transformer.transform(Source, Result)} time.
-     * </p>
-     * <p>
-     * The {@link javax.xml.transform.sax.SAXTransformerFactory} extension 
methods ({@code newTransformerHandler(..)}, {@code newTemplatesHandler()},
-     * {@code newXMLFilter(..)}), if reachable by casting the returned 
factory, produce objects carrying the same guarantees.
-     * </p>
-     *
-     * @return A hardened factory.
-     * @throws IllegalStateException if a required hardening setting cannot be 
applied to the underlying implementation.
-     */
-    public static TransformerFactory newTransformerFactory() {
-        return TransformerHardener.harden(TransformerFactory.newInstance());
-    }
-
-    /**
-     * Returns a new, hardened {@link XMLInputFactory}.
-     * <p>
-     * The three universal guarantees on {@link XmlFactories} apply; StAX 
exposes no additional vectors beyond them.
-     * </p>
-     *
-     * @return A hardened factory.
-     * @throws IllegalStateException     Thrown if a required hardening 
setting cannot be applied to the underlying implementation.
-     * @throws FactoryConfigurationError Thrown if an instance of this factory 
cannot be loaded.
-     */
-    public static XMLInputFactory newXMLInputFactory() {
-        return StaxHardener.harden(XMLInputFactory.newInstance());
-    }
-
-    /**
-     * Returns a new, hardened {@link XPathFactory} for the default XPath 
object model.
-     * <p>
-     * Beyond the three universal guarantees on {@link XmlFactories}, 
URI-fetching XPath 3.1+ functions ({@code doc()}, {@code collection()},
-     * {@code unparsed-text()}) are not resolved.
-     * </p>
-     * <p>
-     * The guarantees also cover the document parse behind {@code 
XPath.evaluate(String, InputSource)} and {@code 
XPathExpression.evaluate(InputSource)}: the
-     * input document is built through a hardened, namespace-aware {@link 
javax.xml.parsers.DocumentBuilder} instead of the engine's internal parser.
-     * </p>
-     *
-     * @return A hardened factory.
-     * @throws IllegalStateException Thrown if a required hardening setting 
cannot be applied to the underlying implementation.
-     * @throws RuntimeException      Thrown if there is a failure in creating 
an {@link XPathFactory} for the default object model.
-     */
-    public static XPathFactory newXPathFactory() {
-        return XPathHardener.harden(XPathFactory.newInstance());
-    }
-
-    private XmlFactories() {
-        // static only
-    }
-}
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 7cb62d3..c9e3f7a 100644
--- a/src/main/java/org/apache/commons/xml/package-info.java
+++ b/src/main/java/org/apache/commons/xml/package-info.java
@@ -14,10 +14,53 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 /**
  * Apache Commons XML provides secure-by-default JAXP factory creation for 
Java. A single method call returns a hardened JAXP factory that can be used to
- * <em>safely</em> parse XML files. The entry point is the class {@link 
org.apache.commons.xml.XmlFactories}.
+ * <em>safely</em> parse XML files.
+ * <p>
+ * Every method returns <em>new, hardened</em> factory instances. No caching 
or pooling is performed; callers on a hot path are responsible for their own
+ * caching.
+ * </p>
+ * <h2>Hardening guarantees</h2>
+ * <p>
+ * Every factory returned by makes the same three guarantees, regardless of 
which JAXP implementation is on the classpath:
+ * </p>
+ * <ul>
+ * <li><strong>External DTDs are not fetched.</strong></li>
+ * <li><strong>External entities are not resolved.</strong></li>
+ * <li><strong>Internal entity expansion is bounded</strong> by the platform's 
secure-processing limit, so DoS payloads such as Billion Laughs are rejected
+ * before they exhaust resources.</li>
+ * </ul>
+ * <p>
+ * These guarantees are defined on OpenJDK 8 or later (and JDK distributions 
built from it). No version of Android supports
+ * {@link javax.xml.XMLConstants#FEATURE_SECURE_PROCESSING}, so on Android 
(API level 19 or later) the hardening is applied as best-effort without a 
guarantee,
+ * tested as complete starting with API level 33; see the threat model's 
"Assumptions about the environment".
+ * </p>
+ * <p>
+ * The guarantees hold whether or not the caller opts into DTD validation 
({@link javax.xml.parsers.DocumentBuilderFactory#setValidating(boolean)
+ * setValidating(true)}) or attaches a compiled XSD via {@link 
javax.xml.parsers.DocumentBuilderFactory#setSchema(javax.xml.validation.Schema) 
setSchema}: every
+ * external resource the validation would otherwise fetch (the DTD itself, an 
{@code xsi:schemaLocation} hint, an external entity referenced from the DTD)
+ * remains blocked.
+ * </p>
+ * <p>
+ * Each method adds factory-specific guarantees on top of the three above, 
documented on the corresponding {@code newXxxFactory()} method.
+ * </p>
+ * <p>
+ * An unresolved external reference resolves to empty content by default, so 
the parse continues without the resource. To reject it with an exception 
instead,
+ * set the system property {@code org.apache.commons.xml.throwOnUnresolved} to 
{@code true}; the property is read at resolution time, and references resolved 
by
+ * a caller-supplied resolver are unaffected.
+ * </p>
+ * <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 hardening 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>
+ * The returned factories inherit the thread-safety properties of the 
underlying JAXP implementation, which in practice means they are <strong>not 
guaranteed to
+ * be thread-safe</strong>. Create a new factory per thread or synchronize 
externally.
+ * </p>
  */
 
 package org.apache.commons.xml;
diff --git a/src/site/markdown/index.md b/src/site/markdown/index.md
index 0a88d1b..42eea29 100644
--- a/src/site/markdown/index.md
+++ b/src/site/markdown/index.md
@@ -40,7 +40,7 @@ such as standalone Xerces, Woodstox, or Saxon's TrAX, need 
further configuration
 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 `XmlFactories` call returns a new 
factory hardened by an
+This library provides that baseline. Each `org.apache.commons.xml` factory 
call returns a new factory hardened 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 hardening setting becomes available or a default changes.
@@ -57,7 +57,7 @@ Add the library to your build:
 </dependency>
 ```
 
-Every method on `XmlFactories` returns a new, hardened factory.
+Every factory method in `org.apache.commons.xml` returns a new, hardened 
factory.
 Pick the one that matches the API you already use;
 no other configuration is required.
 On hardened factories an external resource reference (DTD, entity, schema, 
stylesheet) is never fetched:
@@ -80,7 +80,7 @@ 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 hardening 
recipe, every `XmlFactories` method throws
+a factory resolves to an implementation not covered by any bundled hardening 
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.
 
@@ -88,26 +88,26 @@ requires a code change to this library.
 
 ```java
 import org.w3c.dom.Document;
-import org.apache.commons.xml.XmlFactories;
+import org.apache.commons.xml.HardeningDocumentBuilderFactory;
 
-Document doc = 
XmlFactories.newDocumentBuilderFactory().newDocumentBuilder().parse(inputStream);
+Document doc = 
HardeningDocumentBuilderFactory().newInstance().parse(inputStream);
 ```
 
 **SAX parsing** via `SAXParserFactory`:
 
 ```java
-import org.apache.commons.xml.XmlFactories;
+import org.apache.commons.xml.HardeningSAXParserFactory;
 
-XmlFactories.newSAXParserFactory().newSAXParser().parse(inputStream, 
myDefaultHandler);
+HardeningSAXParserFactory().newInstance().parse(inputStream, myDefaultHandler);
 ```
 
 **Streaming (StAX) parsing** via `XMLInputFactory`:
 
 ```java
 import javax.xml.stream.XMLStreamReader;
-import org.apache.commons.xml.XmlFactories;
+import org.apache.commons.xml.HardeningXMLInputFactory;
 
-XMLStreamReader reader = 
XmlFactories.newXMLInputFactory().createXMLStreamReader(inputStream);
+XMLStreamReader reader = 
HardeningXMLInputFactory.newInstance().createXMLStreamReader(inputStream);
 ```
 
 **XSLT transforms** via `TransformerFactory`:
@@ -115,9 +115,9 @@ XMLStreamReader reader = 
XmlFactories.newXMLInputFactory().createXMLStreamReader
 ```java
 import javax.xml.transform.stream.StreamSource;
 import javax.xml.transform.stream.StreamResult;
-import org.apache.commons.xml.XmlFactories;
+import org.apache.commons.xml.HardeningTransformerFactory;
 
-XmlFactories.newTransformerFactory()
+HardeningTransformerFactory.newInstance()
         .newTransformer(new StreamSource(stylesheet))
         .transform(new StreamSource(inputStream), new 
StreamResult(outputStream));
 ```
@@ -127,9 +127,9 @@ XmlFactories.newTransformerFactory()
 ```java
 import javax.xml.xpath.XPathConstants;
 import org.w3c.dom.NodeList;
-import org.apache.commons.xml.XmlFactories;
+import org.apache.commons.xml.HardeningXPathFactory;
 
-NodeList hits = (NodeList) XmlFactories.newXPathFactory()
+NodeList hits = (NodeList) HardeningXPathFactory.newInstance()
         .newXPath()
         .evaluate("//item", doc, XPathConstants.NODESET);
 ```
@@ -139,9 +139,9 @@ NodeList hits = (NodeList) XmlFactories.newXPathFactory()
 ```java
 import javax.xml.XMLConstants;
 import javax.xml.transform.stream.StreamSource;
-import org.apache.commons.xml.XmlFactories;
+import org.apache.commons.xml.HardeningSchemaFactory;
 
-XmlFactories.newSchemaFactory(XMLConstants.W3C_XML_SCHEMA_NS_URI)
+HardeningSchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI)
         .newSchema(new StreamSource(xsdStream))
         .newValidator()
         .validate(new StreamSource(inputStream));
@@ -152,7 +152,7 @@ 
XmlFactories.newSchemaFactory(XMLConstants.W3C_XML_SCHEMA_NS_URI)
 The hardening 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 hardened 
(Saxon's TrAX is one such case, see Building
-below). Treat stylesheets and schemas as trusted input, or pre-parse them 
through a hardened `XmlFactories` parser and
+below). Treat stylesheets and schemas as trusted input, or pre-parse them 
through a hardened `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 hardening governs reads only,
@@ -162,7 +162,7 @@ so restrict output destinations yourself when running an 
untrusted stylesheet
 ### Transformer handlers and filters
 
 The `SAXTransformerFactory` extension methods, `newTransformerHandler(...)`, 
`newTemplatesHandler()` and `newXMLFilter(...)`,
-if reachable by casting the factory from 
`XmlFactories.newTransformerFactory()`,
+if reachable by casting the factory from 
`HardeningTransformerFactory.newInstance()`,
 produce handlers, filters and `Templates` carrying the same hardening as the 
standard entry points:
 runtime `document()` resolves to empty content,
 and a filter with no caller-set parent parses its input through a hardened 
reader.
@@ -172,7 +172,7 @@ See the [Threat Model](threat_model.html) for the exact 
scope.
 
 ### Caching and thread-safety
 
-There is no caching or pooling inside `XmlFactories`; callers on a hot path 
are responsible for their own caching. The
+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.
 
diff --git a/src/site/markdown/threat_model.md 
b/src/site/markdown/threat_model.md
index 1a688c1..0a350bd 100644
--- a/src/site/markdown/threat_model.md
+++ b/src/site/markdown/threat_model.md
@@ -40,12 +40,12 @@ a finding that falls under [What is out of 
scope](#what-is-out-of-scope) will be
 
 ### Scope and intended use
 
-This library is a helper for **safely creating JAXP factories**. Each 
`XmlFactories.newXxxFactory()` method returns a
+This library is a helper for **safely creating JAXP factories**. Each 
`XxxFactory.newYyy()` method returns a
 new, hardened factory whose parsers reject the common XML attacks (external 
entity / DTD resolution, XXE, SSRF through
 external references, and entity-expansion denial of service such as Billion 
Laughs). The exact guarantee each factory
 makes is documented in the Javadoc:
 
-https://commons.apache.org/sandbox/commons-xml/apidocs/org/apache/commons/xml/factory/XmlFactories.html
+https://commons.apache.org/sandbox/commons-xml/apidocs/org/apache/commons/xml/package-summary.html
 
 The hardening applies to the factory and to the parsers, readers, 
transformers, validators, schemas and XPath objects it produces.
 It governs what those objects read;
@@ -59,7 +59,7 @@ document tries to reach through an entity, DTD, schema, 
stylesheet, or XInclude
 exists to stop that untrusted document from reading local resources, reaching 
the network, or exhausting
 memory or CPU.
 
-The trust boundary is the factory as returned by `XmlFactories`. The XML 
handed to a parser, reader,
+The trust boundary is the factory as returned by `org.apache.commons.xml`. The 
XML handed to a parser, reader,
 transformer, validator or schema produced by that factory is **untrusted**; 
the configuration of the factory
 is **trusted**, and keeping it as delivered is the caller's responsibility. A 
caller running in the same
 process can always reconfigure or replace the factory, so such a caller is not 
an adversary this model
@@ -77,7 +77,7 @@ because your reader's settings are indistinguishable from 
configuration you chos
 
 ### What is in scope
 
-- The hardening recipes applied by `XmlFactories`.
+- The hardening recipes applied by `org.apache.commons.xml`.
   Every implementation of JAXP 1.4 or later is in scope,
   as long as it respects the contract of the features, attributes, and 
properties the recipes use.
   An implementation that cannot accept a required setting makes the factory 
method throw
@@ -85,7 +85,7 @@ because your reader's settings are indistinguishable from 
configuration you chos
 
   The recipes for Android's Expat/KXmlParser are applied as best-effort and 
carry no guarantee
   (see **Supported runtimes** under [Assumptions about the 
environment](#assumptions-about-the-environment)).
-- A factory returned by `XmlFactories`, used as delivered, that fails to 
provide a guarantee the Javadoc states it
+- A factory returned by `org.apache.commons.xml`, used as delivered, that 
fails to provide a guarantee the Javadoc states it
   provides. The guarantee covers the documented entry points of each returned 
factory type,
   including the `SAXTransformerFactory` extension methods when the returned 
`TransformerFactory` exposes them.
 
@@ -95,7 +95,7 @@ The library does not open network connections,
 spawn processes,
 install signal handlers,
 or read environment variables of its own:
-each `XmlFactories` method only configures and returns a JAXP factory.
+each `org.apache.commons.xml` factory method only configures and returns a 
JAXP factory.
 Which hardening recipe applies depends on the JAXP implementation present on 
the classpath.
 
 **Supported runtimes**
@@ -234,13 +234,13 @@ and reports against a factory reconfigured in any of the 
ways below are out of s
   `StreamSource(systemId)`, a `SAXSource` built from a system id) is fetched 
as-is by the JAXP implementation without
   consulting the hardening layer. Restrict it yourself if the URI is untrusted.
 - **Caller-supplied parser instances.**
-  A parser built outside `XmlFactories` and handed to a produced instance is 
used as configured:
+  A parser built outside `org.apache.commons.xml` and handed to a produced 
instance is used as configured:
   a `SAXSource` carrying its own `XMLReader`,
   a `StAXSource` carrying a stream or event reader,
   or a `DOMSource` holding a document parsed elsewhere.
   Its settings are yours, including permissive ones.
   To parse with your own reader under the hardening guarantees,
-  obtain it from `XmlFactories.newSAXParserFactory()`
+  obtain it from `HardeningSAXParserFactory.newInstance()`
   before wrapping it in a `SAXSource`.
 - The behavior of a JAXP implementation that does not respect the contract of 
the settings a hardening recipe requires
   (the factory method throws rather than returning an unhardened factory),
@@ -270,7 +270,7 @@ re-establishing any protection you remove.
 XML-security scanners and static analyzers routinely flag the parsers this 
library produces. The following
 are **not** vulnerabilities under this model:
 
-- A claim that a factory or instance produced by `XmlFactories` is unsafe, 
without showing that a reserved
+- A claim that a factory or instance produced by `org.apache.commons.xml` is 
unsafe, without showing that a reserved
   setting was loosened, a resolver was installed, or an untrusted top-level 
URI was passed (see
   [Assumptions about the environment](#assumptions-about-the-environment) and
   [What is out of scope](#what-is-out-of-scope)). As delivered, the instance 
is hardened; the bare presence
@@ -296,7 +296,7 @@ are **not** vulnerabilities under this model:
   instruction of a stylesheet
   (see **Transform output destinations** under [What is out of 
scope](#what-is-out-of-scope)).
 - Reports in a JAXP implementation that does not respect the contract of the 
settings a hardening recipe
-  requires: `XmlFactories` throws rather than returning an unhardened factory, 
so there is no instance to attack.
+  requires: `org.apache.commons.xml` factory method throws rather than 
returning an unhardened factory, so there is no instance to attack.
 
 ### Triage dispositions
 
@@ -314,7 +314,7 @@ A report judged against this model receives exactly one of:
 ### Conditions that would change this model
 
 Revise this model when any of the following change:
-a new `XmlFactories` factory method or other public surface;
+a new `org.apache.commons.xml` factory or other public surface;
 support for a JAXP implementation beyond those listed under [What is in 
scope](#what-is-in-scope);
 a change to the supported runtimes (see **Supported runtimes** under 
[Assumptions about the environment](#assumptions-about-the-environment));
 a new reserved setting;
diff --git a/src/test/java/org/apache/commons/xml/AssociatedStylesheetTest.java 
b/src/test/java/org/apache/commons/xml/AssociatedStylesheetTest.java
index 3a553fd..4976ed9 100644
--- a/src/test/java/org/apache/commons/xml/AssociatedStylesheetTest.java
+++ b/src/test/java/org/apache/commons/xml/AssociatedStylesheetTest.java
@@ -50,7 +50,7 @@ private static void assertAssociatedStylesheet(final Source 
associated) {
     }
 
     private static TransformerFactory hardenedFactory() {
-        final TransformerFactory factory = 
XmlFactories.newTransformerFactory();
+        final TransformerFactory factory = 
HardeningTransformerFactory.newInstance();
         factory.setErrorListener(AttackTestSupport.STRICT_REPORTER);
         return factory;
     }
diff --git a/src/test/java/org/apache/commons/xml/AttackTestSupport.java 
b/src/test/java/org/apache/commons/xml/AttackTestSupport.java
index a5d87b2..0e3ea63 100644
--- a/src/test/java/org/apache/commons/xml/AttackTestSupport.java
+++ b/src/test/java/org/apache/commons/xml/AttackTestSupport.java
@@ -61,7 +61,7 @@
  * <p>The hardened-side helpers come in three flavors, distinguished by their 
suffix:</p>
  *
  * <ul>
- *   <li>{@code assert*Blocks(...)} runs the payload through a hardened 
factory from {@link XmlFactories} and asserts the parse throws. Used when the 
hardening
+ *   <li>{@code assert*Blocks(...)} runs the payload through a hardened 
factory from {@link org.apache.commons.xml} and asserts the parse throws. Used 
when the hardening
  *       layer is expected to reject the attack outright.</li>
  *   <li>{@code assert*DoesNotLeak(...)} runs the payload through a hardened 
factory and asserts the parse completes without throwing and without producing 
the
  *       {@link #LEAKED_MARKER} string. Used when the hardening contract 
guarantees the parse succeeds but never resolves the external resource (for 
example,
@@ -207,10 +207,10 @@ public void warning(final TransformerException exception) 
{
     /**
      * Asserts a hardened DOM parse of the payload throws.
      *
-     * <p>{@link DocumentBuilder#parse(InputSource)} via {@link 
XmlFactories#newDocumentBuilderFactory()}; only a thrown exception passes.</p>
+     * <p>{@link DocumentBuilder#parse(InputSource)} via {@link 
HardeningDocumentBuilderFactory#newInstance()}; only a thrown exception 
passes.</p>
      */
     static void assertDomBlocks(final String payload) {
-        assertParseFails(() -> 
strictDocumentBuilder(XmlFactories.newDocumentBuilderFactory()).parse(inputSource(payload)),
 "DOM", SAXException.class);
+        assertParseFails(() -> 
strictDocumentBuilder(HardeningDocumentBuilderFactory.newInstance()).parse(inputSource(payload)),
 "DOM", SAXException.class);
     }
 
     /**
@@ -226,7 +226,7 @@ static void assertDomBlocksOrDoesNotLeak(final String 
payload) {
     /**
      * Asserts a hardened DOM parse completes without throwing and without 
leaked content.
      *
-     * <p>{@link DocumentBuilder#parse(InputSource)} via {@link 
XmlFactories#newDocumentBuilderFactory()}; use this when the hardening 
guarantee is "the parse
+     * <p>{@link DocumentBuilder#parse(InputSource)} via {@link 
HardeningDocumentBuilderFactory#newInstance()}; use this when the hardening 
guarantee is "the parse
      * succeeds but never resolves the external resource", for example, when 
the ignore-all resolver floor resolves the external subset to empty content.</p>
      */
     static void assertDomDoesNotLeak(final String payload) {
@@ -236,10 +236,10 @@ static void assertDomDoesNotLeak(final String payload) {
     /**
      * Asserts a hardened DOM parse succeeds.
      *
-     * <p>{@link DocumentBuilder#parse(InputSource)} via {@link 
XmlFactories#newDocumentBuilderFactory()}; positive control for DOCTYPE-only 
payloads.</p>
+     * <p>{@link DocumentBuilder#parse(InputSource)} via {@link 
HardeningDocumentBuilderFactory#newInstance()}; positive control for 
DOCTYPE-only payloads.</p>
      */
     static void assertDomParses(final String payload) {
-        assertParseSucceeds(() -> 
strictDocumentBuilder(XmlFactories.newDocumentBuilderFactory()).parse(inputSource(payload)),
 "DOM");
+        assertParseSucceeds(() -> 
strictDocumentBuilder(HardeningDocumentBuilderFactory.newInstance()).parse(inputSource(payload)),
 "DOM");
     }
 
     /**
@@ -444,45 +444,45 @@ static void assertPermissiveValidatorValidates(final 
String xml) {
     /**
      * Asserts a hardened SAX parse of the payload throws.
      *
-     * <p>{@link XMLReader#parse(InputSource)} on a parser from {@link 
XmlFactories#newSAXParserFactory()}; only a thrown exception passes.</p>
+     * <p>{@link XMLReader#parse(InputSource)} on a parser from {@link 
HardeningSAXParserFactory#newInstance()}; only a thrown exception passes.</p>
      */
     static void assertSaxBlocks(final String payload) {
-        assertParseFails(() -> 
consumeXmlReader(strictXMLReader(XmlFactories.newSAXParserFactory()), payload), 
"SAX", SAXException.class);
+        assertParseFails(() -> 
consumeXmlReader(strictXMLReader(HardeningSAXParserFactory.newInstance()), 
payload), "SAX", SAXException.class);
     }
 
     /**
      * Asserts a hardened SAX parse either blocks at parse or completes 
without leaked content. See {@link #assertDomBlocksOrDoesNotLeak(String)}.
      */
     static void assertSaxBlocksOrDoesNotLeak(final String payload) {
-        assertNoLeakOrThrows(() -> 
captureCharacters(strictXMLReader(XmlFactories.newSAXParserFactory()), 
payload), "SAX", SAXException.class);
+        assertNoLeakOrThrows(() -> 
captureCharacters(strictXMLReader(HardeningSAXParserFactory.newInstance()), 
payload), "SAX", SAXException.class);
     }
 
     /**
      * Asserts a hardened SAX parse completes without throwing and without 
leaked content.
      *
-     * <p>{@link XMLReader#parse(InputSource)} on a parser from {@link 
XmlFactories#newSAXParserFactory()}; use this when the hardening guarantee is 
"the parse
+     * <p>{@link XMLReader#parse(InputSource)} on a parser from {@link 
HardeningSAXParserFactory#newInstance()}; use this when the hardening guarantee 
is "the parse
      * succeeds but never resolves the external resource", for example, when 
the ignore-all resolver floor resolves the external subset to empty content.</p>
      */
     static void assertSaxDoesNotLeak(final String payload) {
-        assertNoLeakStrict(() -> 
captureCharacters(strictXMLReader(XmlFactories.newSAXParserFactory()), 
payload), "SAX");
+        assertNoLeakStrict(() -> 
captureCharacters(strictXMLReader(HardeningSAXParserFactory.newInstance()), 
payload), "SAX");
     }
 
     /**
      * Asserts a hardened SAX parse succeeds.
      *
-     * <p>{@link XMLReader#parse(InputSource)} on a parser from {@link 
XmlFactories#newSAXParserFactory()}; positive control for DOCTYPE-only 
payloads.</p>
+     * <p>{@link XMLReader#parse(InputSource)} on a parser from {@link 
HardeningSAXParserFactory#newInstance()}; positive control for DOCTYPE-only 
payloads.</p>
      */
     static void assertSaxParses(final String payload) {
-        assertParseSucceeds(() -> 
consumeXmlReader(strictXMLReader(XmlFactories.newSAXParserFactory()), payload), 
"SAX");
+        assertParseSucceeds(() -> 
consumeXmlReader(strictXMLReader(HardeningSAXParserFactory.newInstance()), 
payload), "SAX");
     }
 
     /**
      * Asserts a hardened Schema compilation throws.
      *
-     * <p>{@link SchemaFactory#newSchema(Source)} via {@link 
XmlFactories#newSchemaFactory(String)}; only a thrown exception passes.</p>
+     * <p>{@link SchemaFactory#newSchema(Source)} via {@link 
HardeningSchemaFactory#newInstance(String)}; only a thrown exception passes.</p>
      */
     static void assertSchemaBlocks(final Source xsd) {
-        assertParseFails(() -> 
strictSchema(XmlFactories.newSchemaFactory(XMLConstants.W3C_XML_SCHEMA_NS_URI), 
xsd), "Schema compile", SAXException.class, SecurityException.class);
+        assertParseFails(() -> 
strictSchema(HardeningSchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI),
 xsd), "Schema compile", SAXException.class, SecurityException.class);
     }
 
     /**
@@ -491,7 +491,7 @@ static void assertSchemaBlocks(final Source xsd) {
      */
     static void assertSchemaBlocksOrDoesNotLeak(final Source xsd) {
         assertNoLeakOrThrows(() -> {
-            
strictSchema(XmlFactories.newSchemaFactory(XMLConstants.W3C_XML_SCHEMA_NS_URI), 
xsd);
+            
strictSchema(HardeningSchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI),
 xsd);
             return "";
         }, "Schema compile", SAXException.class, SecurityException.class);
     }
@@ -499,73 +499,73 @@ static void assertSchemaBlocksOrDoesNotLeak(final Source 
xsd) {
     /**
      * Asserts a hardened Schema compilation succeeds.
      *
-     * <p>{@link SchemaFactory#newSchema(Source)} via {@link 
XmlFactories#newSchemaFactory(String)}; positive control for DOCTYPE-only 
payloads.</p>
+     * <p>{@link SchemaFactory#newSchema(Source)} via {@link 
HardeningSchemaFactory#newInstance(String)}; positive control for DOCTYPE-only 
payloads.</p>
      */
     static void assertSchemaCompiles(final Source xsd) {
-        assertParseSucceeds(() -> 
strictSchema(XmlFactories.newSchemaFactory(XMLConstants.W3C_XML_SCHEMA_NS_URI), 
xsd), "Schema compile");
+        assertParseSucceeds(() -> 
strictSchema(HardeningSchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI),
 xsd), "Schema compile");
     }
 
     /**
      * Asserts a hardened Schema compilation completes without throwing.
      *
-     * <p>{@link SchemaFactory#newSchema(Source)} via {@link 
XmlFactories#newSchemaFactory(String)}; use this when the hardening contract 
guarantees the compile
+     * <p>{@link SchemaFactory#newSchema(Source)} via {@link 
HardeningSchemaFactory#newInstance(String)}; use this when the hardening 
contract guarantees the compile
      * succeeds but never resolves the external resource (for example, {@code 
XERCES_LOAD_EXTERNAL_DTD=false} silently skipping the external subset, with the 
body's
      * undeclared entity reference dropped per XML 1.0 §4.1).</p>
      */
     static void assertSchemaDoesNotLeak(final Source xsd) {
-        assertParseSucceeds(() -> 
strictSchema(XmlFactories.newSchemaFactory(XMLConstants.W3C_XML_SCHEMA_NS_URI), 
xsd), "Schema compile");
+        assertParseSucceeds(() -> 
strictSchema(HardeningSchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI),
 xsd), "Schema compile");
     }
 
     /**
      * Asserts a hardened StAX parse of the payload throws.
      *
-     * <p>{@link XMLStreamReader} and {@link XMLEventReader} from {@link 
XmlFactories#newXMLInputFactory()}; both flavors are exercised and either must
+     * <p>{@link XMLStreamReader} and {@link XMLEventReader} from {@link 
HardeningXMLInputFactory#newInstance()}; both flavors are exercised and either 
must
      * throw.</p>
      */
     static void assertStaxBlocks(final String payload) {
-        assertParseFails(() -> 
consumeStreamReader(XmlFactories.newXMLInputFactory(), payload), "StAX stream", 
XMLStreamException.class);
-        assertParseFails(() -> 
consumeEventReader(XmlFactories.newXMLInputFactory(), payload), "StAX event", 
XMLStreamException.class);
+        assertParseFails(() -> 
consumeStreamReader(HardeningXMLInputFactory.newInstance(), payload), "StAX 
stream", XMLStreamException.class);
+        assertParseFails(() -> 
consumeEventReader(HardeningXMLInputFactory.newInstance(), payload), "StAX 
event", XMLStreamException.class);
     }
 
     /**
      * Asserts a hardened StAX parse (stream and event) either blocks at parse 
or completes without leaked content. See {@link 
#assertDomBlocksOrDoesNotLeak(String)}.
      */
     static void assertStaxBlocksOrDoesNotLeak(final String payload) {
-        assertNoLeakOrThrows(() -> 
captureStaxStreamText(XmlFactories.newXMLInputFactory(), payload), "StAX 
stream", XMLStreamException.class);
-        assertNoLeakOrThrows(() -> 
captureStaxEventText(XmlFactories.newXMLInputFactory(), payload), "StAX event", 
XMLStreamException.class);
+        assertNoLeakOrThrows(() -> 
captureStaxStreamText(HardeningXMLInputFactory.newInstance(), payload), "StAX 
stream", XMLStreamException.class);
+        assertNoLeakOrThrows(() -> 
captureStaxEventText(HardeningXMLInputFactory.newInstance(), payload), "StAX 
event", XMLStreamException.class);
     }
 
     /**
      * Asserts a hardened StAX parse completes without throwing and without 
leaked content.
      *
-     * <p>{@link XMLStreamReader} and {@link XMLEventReader} from {@link 
XmlFactories#newXMLInputFactory()}; both flavors are exercised. Use this when 
the
+     * <p>{@link XMLStreamReader} and {@link XMLEventReader} from {@link 
HardeningXMLInputFactory#newInstance()}; both flavors are exercised. Use this 
when the
      * hardening guarantee is "the parse succeeds but never resolves the 
external resource", for example, when the JDK's {@code ignore-external-dtd} 
property silently
      * skips the external subset.</p>
      */
     static void assertStaxDoesNotLeak(final String payload) {
-        assertNoLeakStrict(() -> 
captureStaxStreamText(XmlFactories.newXMLInputFactory(), payload), "StAX 
stream");
-        assertNoLeakStrict(() -> 
captureStaxEventText(XmlFactories.newXMLInputFactory(), payload), "StAX event");
+        assertNoLeakStrict(() -> 
captureStaxStreamText(HardeningXMLInputFactory.newInstance(), payload), "StAX 
stream");
+        assertNoLeakStrict(() -> 
captureStaxEventText(HardeningXMLInputFactory.newInstance(), payload), "StAX 
event");
     }
 
     /**
      * Asserts a hardened StAX parse succeeds.
      *
-     * <p>{@link XMLStreamReader} and {@link XMLEventReader} from {@link 
XmlFactories#newXMLInputFactory()}; positive control for DOCTYPE-only 
payloads.</p>
+     * <p>{@link XMLStreamReader} and {@link XMLEventReader} from {@link 
HardeningXMLInputFactory#newInstance()}; positive control for DOCTYPE-only 
payloads.</p>
      */
     static void assertStaxParses(final String payload) {
-        assertParseSucceeds(() -> 
consumeStreamReader(XmlFactories.newXMLInputFactory(), payload), "StAX stream");
-        assertParseSucceeds(() -> 
consumeEventReader(XmlFactories.newXMLInputFactory(), payload), "StAX event");
+        assertParseSucceeds(() -> 
consumeStreamReader(HardeningXMLInputFactory.newInstance(), payload), "StAX 
stream");
+        assertParseSucceeds(() -> 
consumeEventReader(HardeningXMLInputFactory.newInstance(), payload), "StAX 
event");
     }
 
     /**
      * Asserts a hardened Templates compile-and-transform throws.
      *
-     * <p>{@link TransformerFactory#newTemplates(Source)} via {@link 
XmlFactories#newTransformerFactory()} followed by transform; either step 
throwing
+     * <p>{@link TransformerFactory#newTemplates(Source)} via {@link 
HardeningTransformerFactory#newInstance()} followed by transform; either step 
throwing
      * passes.</p>
      */
     static void assertTemplatesBlocks(final Source xslt) {
         assertParseFails(() -> {
-            final Templates templates = 
strictTemplates(XmlFactories.newTransformerFactory(), xslt);
+            final Templates templates = 
strictTemplates(HardeningTransformerFactory.newInstance(), xslt);
             // Xalan returns `null` if the template fails
             if (templates == null) {
                 throw new TransformerException("Transformer factory returned 
null");
@@ -584,7 +584,7 @@ static void assertTemplatesBlocksOrDoesNotLeak(final Source 
xslt) {
     /**
      * Asserts a hardened Templates compile-and-transform succeeds.
      *
-     * <p>{@link TransformerFactory#newTemplates(Source)} via {@link 
XmlFactories#newTransformerFactory()} followed by transform; positive control 
for
+     * <p>{@link TransformerFactory#newTemplates(Source)} via {@link 
HardeningTransformerFactory#newInstance()} followed by transform; positive 
control for
      * DOCTYPE-only payloads.</p>
      */
     static void assertTemplatesCompiles(final Source xslt) {
@@ -594,7 +594,7 @@ static void assertTemplatesCompiles(final Source xslt) {
     /**
      * Asserts a hardened Templates compile-and-transform completes without 
throwing and without leaked content.
      *
-     * <p>{@link TransformerFactory#newTemplates(Source)} via {@link 
XmlFactories#newTransformerFactory()} followed by transform; use this when the 
hardening
+     * <p>{@link TransformerFactory#newTemplates(Source)} via {@link 
HardeningTransformerFactory#newInstance()} followed by transform; use this when 
the hardening
      * contract guarantees the compile and transform succeed but never resolve 
the external resource.</p>
      */
     static void assertTemplatesDoesNotLeak(final Source xslt) {
@@ -604,12 +604,12 @@ static void assertTemplatesDoesNotLeak(final Source xslt) 
{
     /**
      * Asserts a hardened identity Transformer of the payload throws.
      *
-     * <p>{@link Transformer#transform(Source, javax.xml.transform.Result)} on 
the identity transformer from {@link XmlFactories#newTransformerFactory()}; only
+     * <p>{@link Transformer#transform(Source, javax.xml.transform.Result)} on 
the identity transformer from {@link 
HardeningTransformerFactory#newInstance()}; only
      * a thrown exception passes.</p>
      */
     static void assertTransformerBlocks(final String payload) {
         assertParseFails(
-                () -> 
strictTransformer(XmlFactories.newTransformerFactory()).transform(streamSource(payload),
 new StreamResult(new StringWriter())),
+                () -> 
strictTransformer(HardeningTransformerFactory.newInstance()).transform(streamSource(payload),
 new StreamResult(new StringWriter())),
                 "Transformer", TransformerException.class);
     }
 
@@ -623,7 +623,7 @@ static void assertTransformerBlocksOrDoesNotLeak(final 
String payload) {
     /**
      * Asserts a hardened identity Transformer completes without throwing and 
without leaked content.
      *
-     * <p>{@link Transformer#transform(Source, javax.xml.transform.Result)} 
via {@link XmlFactories#newTransformerFactory()}; use this when the hardening
+     * <p>{@link Transformer#transform(Source, javax.xml.transform.Result)} 
via {@link HardeningTransformerFactory#newInstance()}; use this when the 
hardening
      * contract guarantees the transform succeeds but never resolves the 
external resource.</p>
      */
     static void assertTransformerDoesNotLeak(final String payload) {
@@ -633,7 +633,7 @@ static void assertTransformerDoesNotLeak(final String 
payload) {
     /**
      * Asserts a hardened identity Transformer succeeds.
      *
-     * <p>{@link Transformer#transform(Source, javax.xml.transform.Result)} on 
the identity transformer from {@link XmlFactories#newTransformerFactory()};
+     * <p>{@link Transformer#transform(Source, javax.xml.transform.Result)} on 
the identity transformer from {@link HardeningTransformerFactory#newInstance()};
      * positive control for DOCTYPE-only payloads.</p>
      */
     static void assertTransformerTransforms(final String payload) {
@@ -643,12 +643,12 @@ static void assertTransformerTransforms(final String 
payload) {
     /**
      * Asserts a hardened Validator validation throws.
      *
-     * <p>{@link Validator#validate(Source)} on a validator from {@link 
#BENIGN_SCHEMA} compiled via {@link XmlFactories#newSchemaFactory(String)}; 
only a thrown
+     * <p>{@link Validator#validate(Source)} on a validator from {@link 
#BENIGN_SCHEMA} compiled via {@link 
HardeningSchemaFactory#newInstance(String)}; only a thrown
      * exception passes (the schema is benign; the attack lives in the 
instance document).</p>
      */
     static void assertValidatorBlocks(final String xml) {
         assertParseFails(
-                () -> 
strictValidator(strictSchema(XmlFactories.newSchemaFactory(XMLConstants.W3C_XML_SCHEMA_NS_URI),
 streamSource(BENIGN_SCHEMA))).validate(streamSource(xml)),
+                () -> 
strictValidator(strictSchema(HardeningSchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI),
 streamSource(BENIGN_SCHEMA))).validate(streamSource(xml)),
                 "Validator", SAXException.class, SecurityException.class);
     }
 
@@ -659,7 +659,7 @@ static void assertValidatorBlocks(final String xml) {
      */
     static void assertValidatorBlocksOrDoesNotLeak(final String xml) {
         assertNoLeakOrThrows(() -> {
-            
strictValidator(strictSchema(XmlFactories.newSchemaFactory(XMLConstants.W3C_XML_SCHEMA_NS_URI),
 streamSource(BENIGN_SCHEMA))).validate(streamSource(xml));
+            
strictValidator(strictSchema(HardeningSchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI),
 streamSource(BENIGN_SCHEMA))).validate(streamSource(xml));
             return "";
         }, "Validator", SAXException.class, SecurityException.class, 
IOException.class);
     }
@@ -667,24 +667,24 @@ static void assertValidatorBlocksOrDoesNotLeak(final 
String xml) {
     /**
      * Asserts a hardened Validator validation completes without throwing.
      *
-     * <p>{@link Validator#validate(Source)} on a validator from {@link 
#BENIGN_SCHEMA} compiled via {@link XmlFactories#newSchemaFactory(String)}; use 
this when the
+     * <p>{@link Validator#validate(Source)} on a validator from {@link 
#BENIGN_SCHEMA} compiled via {@link 
HardeningSchemaFactory#newInstance(String)}; use this when the
      * hardening contract guarantees the validate succeeds but never resolves 
the external resource.</p>
      */
     static void assertValidatorDoesNotLeak(final String xml) {
         assertParseSucceeds(
-                () -> 
strictValidator(strictSchema(XmlFactories.newSchemaFactory(XMLConstants.W3C_XML_SCHEMA_NS_URI),
 streamSource(BENIGN_SCHEMA))).validate(streamSource(xml)),
+                () -> 
strictValidator(strictSchema(HardeningSchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI),
 streamSource(BENIGN_SCHEMA))).validate(streamSource(xml)),
                 "Validator");
     }
 
     /**
      * Asserts a hardened Validator validation succeeds.
      *
-     * <p>{@link Validator#validate(Source)} on a validator from {@link 
#BENIGN_SCHEMA} compiled via {@link XmlFactories#newSchemaFactory(String)}; 
positive control
+     * <p>{@link Validator#validate(Source)} on a validator from {@link 
#BENIGN_SCHEMA} compiled via {@link 
HardeningSchemaFactory#newInstance(String)}; positive control
      * for DOCTYPE-only payloads.</p>
      */
     static void assertValidatorValidates(final String xml) {
         assertParseSucceeds(
-                () -> 
strictValidator(strictSchema(XmlFactories.newSchemaFactory(XMLConstants.W3C_XML_SCHEMA_NS_URI),
 streamSource(BENIGN_SCHEMA))).validate(streamSource(xml)),
+                () -> 
strictValidator(strictSchema(HardeningSchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI),
 streamSource(BENIGN_SCHEMA))).validate(streamSource(xml)),
                 "Validator");
     }
 
@@ -834,7 +834,7 @@ private static void consumeXmlReader(final XMLReader 
reader, final String payloa
     }
 
     private static String domParseAndCaptureText(final String payload) throws 
Exception {
-        final Document doc = 
strictDocumentBuilder(XmlFactories.newDocumentBuilderFactory()).parse(inputSource(payload));
+        final Document doc = 
strictDocumentBuilder(HardeningDocumentBuilderFactory.newInstance()).parse(inputSource(payload));
         if (doc.getDocumentElement() == null) {
             return "";
         }
@@ -845,7 +845,7 @@ private static String domParseAndCaptureText(final String 
payload) throws Except
 
     private static String identityTransformAndCapture(final String payload) 
throws TransformerException {
         final StringWriter sink = new StringWriter();
-        
strictTransformer(XmlFactories.newTransformerFactory()).transform(streamSource(payload),
 new StreamResult(sink));
+        
strictTransformer(HardeningTransformerFactory.newInstance()).transform(streamSource(payload),
 new StreamResult(sink));
         return sink.toString();
     }
 
@@ -1042,7 +1042,7 @@ private static void suppressException(final Executable 
action) {
 
     private static String templatesCompileAndTransform(final Source xslt) 
throws TransformerException {
         final StringWriter sink = new StringWriter();
-        final Templates templates = 
strictTemplates(XmlFactories.newTransformerFactory(), xslt);
+        final Templates templates = 
strictTemplates(HardeningTransformerFactory.newInstance(), xslt);
         // Xalan returns `null` if the template fails
         if (templates != null) {
             strictTransformer(templates).transform(streamSource("<root/>"), 
new StreamResult(sink));
diff --git a/src/test/java/org/apache/commons/xml/DenyUnresolvedTest.java 
b/src/test/java/org/apache/commons/xml/DenyUnresolvedTest.java
index 975ab47..1e91add 100644
--- a/src/test/java/org/apache/commons/xml/DenyUnresolvedTest.java
+++ b/src/test/java/org/apache/commons/xml/DenyUnresolvedTest.java
@@ -29,7 +29,7 @@
 import org.xml.sax.SAXException;
 
 /**
- * Checks that the resolver floors reject unresolved references when the 
{@value XmlFactories#THROW_ON_UNRESOLVED} system property is set.
+ * Checks that the resolver floors reject unresolved references when the 
{@value HardeningException#THROW_ON_UNRESOLVED} system property is set.
  *
  * <p>The floors are exercised directly: with the property set and no caller 
delegate, each must throw its hook's exception instead of resolving to empty
  * content. The property is read at resolution time, so setting it around a 
single test cannot leak into the rest of the suite.</p>
@@ -40,12 +40,12 @@ class DenyUnresolvedTest {
 
     @AfterEach
     void clearThrowOnUnresolved() {
-        System.clearProperty(XmlFactories.THROW_ON_UNRESOLVED);
+        System.clearProperty(HardeningException.THROW_ON_UNRESOLVED);
     }
 
     @BeforeEach
     void enableThrowOnUnresolved() {
-        System.setProperty(XmlFactories.THROW_ON_UNRESOLVED, "true");
+        System.setProperty(HardeningException.THROW_ON_UNRESOLVED, "true");
     }
 
     @Test
diff --git a/src/test/java/org/apache/commons/xml/EntityResolverFloorTest.java 
b/src/test/java/org/apache/commons/xml/EntityResolverFloorTest.java
index e8fe90f..22a943a 100644
--- a/src/test/java/org/apache/commons/xml/EntityResolverFloorTest.java
+++ b/src/test/java/org/apache/commons/xml/EntityResolverFloorTest.java
@@ -119,20 +119,20 @@ private static String entityPayload(final String 
entitySystemId) {
     }
 
     private static XMLInputFactory externalEntityStaxFactory() {
-        final XMLInputFactory factory = XmlFactories.newXMLInputFactory();
+        final XMLInputFactory factory = HardeningXMLInputFactory.newInstance();
         factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, 
true);
         factory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, 
true);
         return factory;
     }
 
     private static DocumentBuilder hardenedBuilder() throws Exception {
-        final DocumentBuilder builder = 
XmlFactories.newDocumentBuilderFactory().newDocumentBuilder();
+        final DocumentBuilder builder = 
HardeningDocumentBuilderFactory.newInstance().newDocumentBuilder();
         builder.setErrorHandler(AttackTestSupport.STRICT_REPORTER);
         return builder;
     }
 
     private static XMLReader hardenedReader() throws Exception {
-        final XMLReader reader = 
XmlFactories.newSAXParserFactory().newSAXParser().getXMLReader();
+        final XMLReader reader = 
HardeningSAXParserFactory.newInstance().newSAXParser().getXMLReader();
         reader.setErrorHandler(AttackTestSupport.STRICT_REPORTER);
         return reader;
     }
@@ -144,7 +144,7 @@ private static XMLReader hardenedReader() throws Exception {
      * implementation cannot quietly recover from a floor resolution while the 
test asserts clean completion.
      */
     private static TransformerFactory hardenedTransformerFactory() {
-        final TransformerFactory factory = 
XmlFactories.newTransformerFactory();
+        final TransformerFactory factory = 
HardeningTransformerFactory.newInstance();
         factory.setErrorListener(AttackTestSupport.STRICT_REPORTER);
         return factory;
     }
@@ -168,7 +168,7 @@ private static LSInput lsInput(final String systemId) {
     }
 
     private static DocumentBuilder xIncludeAwareBuilder() throws Exception {
-        final DocumentBuilderFactory factory = 
XmlFactories.newDocumentBuilderFactory();
+        final DocumentBuilderFactory factory = 
HardeningDocumentBuilderFactory.newInstance();
         factory.setNamespaceAware(true);
         AttackTestSupport.assumeDoesNotThrow(() -> 
factory.setXIncludeAware(true));
         final DocumentBuilder builder = factory.newDocumentBuilder();
@@ -177,7 +177,7 @@ private static DocumentBuilder xIncludeAwareBuilder() 
throws Exception {
     }
 
     private static XMLReader xIncludeAwareReader() throws Exception {
-        final SAXParserFactory factory = XmlFactories.newSAXParserFactory();
+        final SAXParserFactory factory = 
HardeningSAXParserFactory.newInstance();
         factory.setNamespaceAware(true);
         AttackTestSupport.assumeDoesNotThrow(() -> 
factory.setXIncludeAware(true));
         final XMLReader reader = factory.newSAXParser().getXMLReader();
@@ -227,7 +227,7 @@ void domResolvesRelativeXIncludeSibling() throws Exception {
     void saxParseWithHandlerDoesNotBypass() throws Exception {
         // SAXParser.parse(source, handler) installs the handler as the 
reader's entity resolver; the handler does not resolve it (returns null), so the
         // ignore-all floor must still resolve the external entity to empty 
rather than letting the parser fetch it.
-        final SAXParser parser = 
XmlFactories.newSAXParserFactory().newSAXParser();
+        final SAXParser parser = 
HardeningSAXParserFactory.newInstance().newSAXParser();
         final StringBuilder text = new StringBuilder();
         try {
             
parser.parse(AttackTestSupport.inputSource(entityPayload(ALLOWED)), 
AttackTestSupport.capturingHandler(text));
@@ -276,7 +276,7 @@ void saxResolvesRelativeXIncludeSibling() throws Exception {
     @Tag("schema")
     void schemaDeniesUnlisted() {
         assertParseFails(() -> {
-            final SchemaFactory factory = 
XmlFactories.newSchemaFactory(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+            final SchemaFactory factory = 
HardeningSchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
             factory.setResourceResolver((type, namespaceURI, publicId, 
systemId, baseURI) -> null);
             
factory.newSchema(AttackTestSupport.resourceSource("with-import.xsd"));
         }, "Schema import", SAXException.class, SecurityException.class);
@@ -287,7 +287,7 @@ void schemaDeniesUnlisted() {
     void schemaFetchesIdentifierOnlyOptIn() {
         // A non-null return is an opt-in even without content: the 
implementation fetches the named resource itself, mirroring the entity floor's 
contract.
         assertParseSucceeds(() -> {
-            final SchemaFactory factory = 
XmlFactories.newSchemaFactory(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+            final SchemaFactory factory = 
HardeningSchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
             factory.setResourceResolver((type, namespaceURI, publicId, 
systemId, baseURI) ->
                     systemId != null && systemId.endsWith("included.xsd") ? 
identifierOnlyLsInput(ALLOWED_SCHEMA) : null);
             
factory.newSchema(AttackTestSupport.resourceSource("with-import.xsd"));
@@ -299,7 +299,7 @@ void schemaFetchesIdentifierOnlyOptIn() {
     void schemaResolvesAllowListed() {
         // with-import.xsd references an element defined only in the imported 
included.xsd, so it compiles only if the import is resolved.
         assertParseSucceeds(() -> {
-            final SchemaFactory factory = 
XmlFactories.newSchemaFactory(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+            final SchemaFactory factory = 
HardeningSchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
             factory.setResourceResolver(SCHEMA_ALLOW_LIST);
             
factory.newSchema(AttackTestSupport.resourceSource("with-import.xsd"));
         }, "Schema import via caller resolver");
@@ -336,7 +336,7 @@ void staxDoesNotLeakUnlisted() throws Exception {
     @Test
     @Tag("stax")
     void staxGetXMLResolverReportsCallerUnwrapped() {
-        final XMLInputFactory factory = XmlFactories.newXMLInputFactory();
+        final XMLInputFactory factory = HardeningXMLInputFactory.newInstance();
         final XMLResolver caller = (publicID, systemID, baseURI, namespace) -> 
null;
         factory.setXMLResolver(caller);
         assertSame(caller, factory.getXMLResolver(), "getXMLResolver should 
report the caller's resolver, not the floor wrapper");
diff --git a/src/test/java/org/apache/commons/xml/XmlFactoriesTest.java 
b/src/test/java/org/apache/commons/xml/HardeningFactoriesSmokeTest.java
similarity index 63%
rename from src/test/java/org/apache/commons/xml/XmlFactoriesTest.java
rename to src/test/java/org/apache/commons/xml/HardeningFactoriesSmokeTest.java
index e61d64b..0ca7aa7 100644
--- a/src/test/java/org/apache/commons/xml/XmlFactoriesTest.java
+++ b/src/test/java/org/apache/commons/xml/HardeningFactoriesSmokeTest.java
@@ -38,41 +38,40 @@
 import org.xml.sax.InputSource;
 
 /**
- * Public-API smoke tests for {@link XmlFactories}.
- *
- * <p>Attack tests live in the {@code attacks} sub-package; this file only 
verifies that new factories are returned, that they report safe defaults, and 
that
- * a benign document still parses successfully.</p>
+ * Public-API smoke tests for {@link org.apache.commons.xml}.
+ * <p>
+ * Attack tests live in the {@code attacks} sub-package; this file only 
verifies that new factories are returned, that they report safe defaults, and 
that a
+ * benign document still parses successfully.
+ * </p>
  */
-class XmlFactoriesTest {
+class HardeningFactoriesSmokeTest {
 
-    private static final String BENIGN_XML =
-            "<?xml version=\"1.0\"?>\n<root><child>hello</child></root>\n";
+    private static final String BENIGN_XML = "<?xml 
version=\"1.0\"?>\n<root><child>hello</child></root>\n";
 
     @Test
     void benignDocumentParses() throws Exception {
-        final Document doc = 
XmlFactories.newDocumentBuilderFactory().newDocumentBuilder().parse(new 
InputSource(new StringReader(BENIGN_XML)));
+        final Document doc = 
HardeningDocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new 
InputSource(new StringReader(BENIGN_XML)));
         assertNotNull(doc);
         assertNotNull(doc.getDocumentElement());
     }
 
     @Test
     void newDocumentBuilderFactoryDisablesXIncludeAndValidation() {
-        final DocumentBuilderFactory factory = 
XmlFactories.newDocumentBuilderFactory();
+        final DocumentBuilderFactory factory = 
HardeningDocumentBuilderFactory.newInstance();
         assertFalse(factory.isXIncludeAware(), "XInclude must be off by 
default");
         assertFalse(factory.isValidating(), "Validation must be off by 
default");
     }
 
     @Test
     void newDocumentBuilderFactoryEnablesSecureProcessing() throws Exception {
-        final DocumentBuilderFactory factory = 
XmlFactories.newDocumentBuilderFactory();
-        assertTrue(factory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING),
-                "FEATURE_SECURE_PROCESSING must be on");
+        final DocumentBuilderFactory factory = 
HardeningDocumentBuilderFactory.newInstance();
+        assertTrue(factory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING), 
"FEATURE_SECURE_PROCESSING must be on");
     }
 
     @Test
     void newDocumentBuilderFactoryReturnsFreshInstance() {
-        final DocumentBuilderFactory a = 
XmlFactories.newDocumentBuilderFactory();
-        final DocumentBuilderFactory b = 
XmlFactories.newDocumentBuilderFactory();
+        final DocumentBuilderFactory a = 
HardeningDocumentBuilderFactory.newInstance();
+        final DocumentBuilderFactory b = 
HardeningDocumentBuilderFactory.newInstance();
         assertNotNull(a);
         assertNotNull(b);
         assertNotSame(a, b);
@@ -80,8 +79,8 @@ void newDocumentBuilderFactoryReturnsFreshInstance() {
 
     @Test
     void newSAXParserFactoryReturnsFreshInstance() {
-        final SAXParserFactory a = XmlFactories.newSAXParserFactory();
-        final SAXParserFactory b = XmlFactories.newSAXParserFactory();
+        final SAXParserFactory a = HardeningSAXParserFactory.newInstance();
+        final SAXParserFactory b = HardeningSAXParserFactory.newInstance();
         assertNotSame(a, b);
         assertFalse(a.isValidating());
         assertFalse(a.isXIncludeAware());
@@ -89,23 +88,23 @@ void newSAXParserFactoryReturnsFreshInstance() {
 
     @Test
     void newSchemaFactoryReturnsFreshInstance() throws Exception {
-        final SchemaFactory a = 
XmlFactories.newSchemaFactory(XMLConstants.W3C_XML_SCHEMA_NS_URI);
-        final SchemaFactory b = 
XmlFactories.newSchemaFactory(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+        final SchemaFactory a = 
HardeningSchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+        final SchemaFactory b = 
HardeningSchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
         assertNotSame(a, b);
         assertTrue(a.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING));
     }
 
     @Test
     void newTransformerFactoryReturnsFreshInstance() {
-        final TransformerFactory a = XmlFactories.newTransformerFactory();
-        final TransformerFactory b = XmlFactories.newTransformerFactory();
+        final TransformerFactory a = HardeningTransformerFactory.newInstance();
+        final TransformerFactory b = HardeningTransformerFactory.newInstance();
         assertNotSame(a, b);
     }
 
     @Test
     void newXMLInputFactoryReturnsFreshInstance() {
-        final XMLInputFactory a = XmlFactories.newXMLInputFactory();
-        final XMLInputFactory b = XmlFactories.newXMLInputFactory();
+        final XMLInputFactory a = HardeningXMLInputFactory.newInstance();
+        final XMLInputFactory b = HardeningXMLInputFactory.newInstance();
         assertNotSame(a, b);
         assertEquals(Boolean.TRUE, a.getProperty(XMLInputFactory.SUPPORT_DTD));
         assertEquals(Boolean.FALSE, 
a.getProperty(XMLInputFactory.IS_VALIDATING));
@@ -113,10 +112,9 @@ void newXMLInputFactoryReturnsFreshInstance() {
 
     @Test
     void newXPathFactoryReturnsFreshInstance() throws Exception {
-        final XPathFactory a = XmlFactories.newXPathFactory();
-        final XPathFactory b = XmlFactories.newXPathFactory();
+        final XPathFactory a = HardeningXPathFactory.newXPathFactory();
+        final XPathFactory b = HardeningXPathFactory.newXPathFactory();
         assertNotSame(a, b);
         assertTrue(a.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING));
     }
-
 }
diff --git a/src/test/java/org/apache/commons/xml/ResetHardeningTest.java 
b/src/test/java/org/apache/commons/xml/ResetHardeningTest.java
index 687df35..6c8cd03 100644
--- a/src/test/java/org/apache/commons/xml/ResetHardeningTest.java
+++ b/src/test/java/org/apache/commons/xml/ResetHardeningTest.java
@@ -59,7 +59,7 @@ private static String entityPayload(final String 
entitySystemId) {
     @Tag("dom")
     void documentBuilderResetKeepsEntityResolverFloor() throws Exception {
         
Assumptions.assumeTrue(AttackTestSupport.DOM_RESOLVES_INTERNAL_ENTITIES, 
"platform DOM does not resolve user-defined entities");
-        final DocumentBuilder builder = 
XmlFactories.newDocumentBuilderFactory().newDocumentBuilder();
+        final DocumentBuilder builder = 
HardeningDocumentBuilderFactory.newInstance().newDocumentBuilder();
         AttackTestSupport.assumeDoesNotThrow(builder::reset);
         try {
             final Document doc = 
builder.parse(AttackTestSupport.inputSource(entityPayload(UNLISTED)));
@@ -72,7 +72,7 @@ void documentBuilderResetKeepsEntityResolverFloor() throws 
Exception {
     @Test
     @Tag("sax")
     void saxParserResetKeepsEntityResolverFloor() throws Exception {
-        final SAXParser parser = 
XmlFactories.newSAXParserFactory().newSAXParser();
+        final SAXParser parser = 
HardeningSAXParserFactory.newInstance().newSAXParser();
         // Materialize the hardened reader before the reset, so a stale cached 
wrapper would be observable.
         parser.getXMLReader();
         AttackTestSupport.assumeDoesNotThrow(parser::reset);
@@ -90,7 +90,7 @@ void saxParserResetKeepsEntityResolverFloor() throws 
Exception {
     @Tag("trax")
     void transformerResetKeepsUriResolverFloor() throws Exception {
         // with-document.xsl copies document('referenced.xml') into the output 
at transform time, so a transformer whose floor was stripped leaks the marker.
-        final Transformer transformer = XmlFactories.newTransformerFactory()
+        final Transformer transformer = 
HardeningTransformerFactory.newInstance()
                 
.newTemplates(AttackTestSupport.resourceSource("with-document.xsl")).newTransformer();
         AttackTestSupport.assumeDoesNotThrow(transformer::reset);
         final StringWriter sink = new StringWriter();
@@ -107,7 +107,7 @@ void transformerResetKeepsUriResolverFloor() throws 
Exception {
     void validatorResetKeepsResourceResolverFloor() throws Exception {
         // A Schema built without sources validates against the instance's 
xsi:schemaLocation hints, so the resolver floor is the only barrier between the
         // validator and the external schema fetch.
-        final Validator validator = 
XmlFactories.newSchemaFactory(XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema().newValidator();
+        final Validator validator = 
HardeningSchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema().newValidator();
         AttackTestSupport.assumeDoesNotThrow(validator::reset);
         validator.setErrorHandler(AttackTestSupport.STRICT_REPORTER);
         // schema-location-instance.xml hints at schema-location.xsd, which 
declares its root: a validator whose floor was stripped fetches it and validates
diff --git a/src/test/java/org/apache/commons/xml/SaxSurfaceTestSupport.java 
b/src/test/java/org/apache/commons/xml/SaxSurfaceTestSupport.java
index 3a52ec9..2f16864 100644
--- a/src/test/java/org/apache/commons/xml/SaxSurfaceTestSupport.java
+++ b/src/test/java/org/apache/commons/xml/SaxSurfaceTestSupport.java
@@ -39,7 +39,7 @@ final class SaxSurfaceTestSupport {
 
     /** Feeds the input's SAX events into the handler through a hardened, 
namespace-aware reader. */
     static void feed(final ContentHandler handler, final InputSource input) 
throws Exception {
-        final SAXParserFactory factory = XmlFactories.newSAXParserFactory();
+        final SAXParserFactory factory = 
HardeningSAXParserFactory.newInstance();
         factory.setNamespaceAware(true);
         final XMLReader reader = factory.newSAXParser().getXMLReader();
         reader.setContentHandler(handler);
@@ -49,7 +49,7 @@ static void feed(final ContentHandler handler, final 
InputSource input) throws E
 
     /** The hardened factory, as its runtime {@link SAXTransformerFactory} 
type. */
     static SAXTransformerFactory hardenedFactory() {
-        return (SAXTransformerFactory) XmlFactories.newTransformerFactory();
+        return (SAXTransformerFactory) 
HardeningTransformerFactory.newInstance();
     }
 
     /** Opens a fixture under {@code leaked/} as an {@link InputSource} 
preserving its system id, so relative hrefs resolve normally. */
diff --git 
a/src/test/java/org/apache/commons/xml/SchemaFactoryLanguageTest.java 
b/src/test/java/org/apache/commons/xml/SchemaFactoryLanguageTest.java
index 80b50e2..584c703 100644
--- a/src/test/java/org/apache/commons/xml/SchemaFactoryLanguageTest.java
+++ b/src/test/java/org/apache/commons/xml/SchemaFactoryLanguageTest.java
@@ -23,7 +23,7 @@
 import org.junit.jupiter.api.Test;
 
 /**
- * Checks that {@link XmlFactories#newSchemaFactory(String)} passes the schema 
language through to {@link javax.xml.validation.SchemaFactory#newInstance}.
+ * Checks that {@link HardeningSchemaFactory#newInstance(String)} passes the 
schema language through to {@link 
javax.xml.validation.SchemaFactory#newInstance}.
  *
  * <p>The working W3C XML Schema path is exercised by the whole schema suite; 
this test covers only the language-selection contract.</p>
  */
@@ -32,7 +32,7 @@ class SchemaFactoryLanguageTest {
 
     @Test
     void unknownSchemaLanguageThrows() {
-        assertThrows(IllegalArgumentException.class, () -> 
XmlFactories.newSchemaFactory("urn:example:unknown-schema-language"),
+        assertThrows(IllegalArgumentException.class, () -> 
HardeningSchemaFactory.newInstance("urn:example:unknown-schema-language"),
                 "an unsupported schema language should surface 
SchemaFactory.newInstance's IllegalArgumentException");
     }
 }
diff --git a/src/test/java/org/apache/commons/xml/SchemaLocationDomTest.java 
b/src/test/java/org/apache/commons/xml/SchemaLocationDomTest.java
index 7cd4c3b..d4700b6 100644
--- a/src/test/java/org/apache/commons/xml/SchemaLocationDomTest.java
+++ b/src/test/java/org/apache/commons/xml/SchemaLocationDomTest.java
@@ -87,7 +87,7 @@ private static boolean supportsSchemaLanguage() {
     @Test
     void hardenedDoesNotFetchExternalSchema() {
         assumeTrue(supportsSchemaLanguage(), "parser does not support JAXP 1.2 
schema-language XSD validation");
-        final DocumentBuilderFactory factory = 
enableXsdValidation(XmlFactories.newDocumentBuilderFactory());
+        final DocumentBuilderFactory factory = 
enableXsdValidation(HardeningDocumentBuilderFactory.newInstance());
         // The schemaLocation reference resolves to empty rather than being 
fetched. Either the empty schema fails the validating parse (acceptable), or the
         // parse completes but the schema's default leak attribute is never 
inlined. Either way the marker must not reach the DOM.
         try {
diff --git 
a/src/test/java/org/apache/commons/xml/SchemaLocationPropertyTest.java 
b/src/test/java/org/apache/commons/xml/SchemaLocationPropertyTest.java
index 127c920..15c9640 100644
--- a/src/test/java/org/apache/commons/xml/SchemaLocationPropertyTest.java
+++ b/src/test/java/org/apache/commons/xml/SchemaLocationPropertyTest.java
@@ -82,7 +82,7 @@ private static <T> T configureOrSkip(final 
ThrowingSupplier<T> setup) {
 
     private static DocumentBuilder hardenedValidatingDom(final String 
property, final String value) {
         return configureOrSkip(() -> {
-            final DocumentBuilderFactory factory = 
XmlFactories.newDocumentBuilderFactory();
+            final DocumentBuilderFactory factory = 
HardeningDocumentBuilderFactory.newInstance();
             factory.setNamespaceAware(true);
             factory.setValidating(true);
             factory.setAttribute(SCHEMA_LANGUAGE, 
XMLConstants.W3C_XML_SCHEMA_NS_URI);
@@ -93,7 +93,7 @@ private static DocumentBuilder hardenedValidatingDom(final 
String property, fina
 
     private static XMLReader hardenedValidatingSax(final String property, 
final String value) {
         return configureOrSkip(() -> {
-            final SAXParserFactory factory = 
XmlFactories.newSAXParserFactory();
+            final SAXParserFactory factory = 
HardeningSAXParserFactory.newInstance();
             factory.setNamespaceAware(true);
             factory.setValidating(true);
             final SAXParser parser = factory.newSAXParser();
diff --git a/src/test/java/org/apache/commons/xml/SchemaLocationSaxTest.java 
b/src/test/java/org/apache/commons/xml/SchemaLocationSaxTest.java
index 7d30e35..0e02342 100644
--- a/src/test/java/org/apache/commons/xml/SchemaLocationSaxTest.java
+++ b/src/test/java/org/apache/commons/xml/SchemaLocationSaxTest.java
@@ -100,7 +100,7 @@ private static boolean supportsSchemaLanguage() {
     @Test
     void hardenedDoesNotFetchExternalSchema() throws Exception {
         assumeTrue(supportsSchemaLanguage(), "parser does not support JAXP 1.2 
schema-language XSD validation");
-        final SAXParser parser = 
newValidatingParser(XmlFactories.newSAXParserFactory());
+        final SAXParser parser = 
newValidatingParser(HardeningSAXParserFactory.newInstance());
         // The schemaLocation reference resolves to empty rather than being 
fetched. Either the empty schema fails the validating parse (acceptable), or the
         // parse completes but the schema's default leak attribute is never 
augmented onto the element. Either way the marker must not be observed.
         final LeakCapturingHandler handler = new LeakCapturingHandler();
diff --git a/src/test/java/org/apache/commons/xml/ShadingFootprintTest.java 
b/src/test/java/org/apache/commons/xml/ShadingFootprintTest.java
index 44af316..2a7342c 100644
--- a/src/test/java/org/apache/commons/xml/ShadingFootprintTest.java
+++ b/src/test/java/org/apache/commons/xml/ShadingFootprintTest.java
@@ -44,7 +44,7 @@
  * <p>Using {@code jdependency}, the same library {@code maven-shade-plugin}'s 
{@code minimizeJar} uses, this test computes each entry point's transitive class
  * closure over the compiled {@code target/classes} and pins it to an expected 
set. It keeps each hardener from silently regaining a dependency on classes it
  * should not need (for example a sibling resolver floor or another hardener), 
so schema builds only on the shared SAX path, TrAX and XPath additionally on the
- * DOM path their Xalan getAssociatedStylesheet and InputSource rewrites parse 
through, while only the public {@link XmlFactories} entry pulls the whole
+ * DOM path their Xalan getAssociatedStylesheet and InputSource rewrites parse 
through, while only the public {@link org.apache.commons.xml} entry pulls the 
whole
  * library. Update the expected sets deliberately: a change here is a change 
to what a downstream shade includes.</p>
  *
  * <p>The test reads the compiled {@code .class} files from the code-source 
location, which only exists on a regular JVM: a native image carries no 
bytecode (and
@@ -87,15 +87,15 @@ class ShadingFootprintTest {
             "HardeningValidatorHandler", "HardeningSchema", 
"FallbackIgnoreLSResourceResolver");
 
     /**
-     * Only the public {@link XmlFactories} entry, which references every 
hardener, still pulls the whole library; this is its class count.
+     * Only the public {@link org.apache.commons.xml} entry, which references 
every hardener, still pulls the whole library; this is its class count.
      */
-    private static final int LIBRARY_CLASS_COUNT = 35;
+    private static final int LIBRARY_CLASS_COUNT = 5;
 
     /**
      * Entry points reported by the {@link #reportFootprint()} diagnostic, 
most-focused first, ending with the whole library.
      */
     private static final String[] REPORTED = {"DocumentBuilderHardener", 
"SAXParserHardener", "StaxHardener", "TransformerHardener", "XPathHardener",
-            "SchemaHardener", "XmlFactories"};
+            "SchemaHardener"};
 
     private static Clazzpath clazzpath;
     private static Path classesDir;
@@ -146,7 +146,7 @@ static void indexCompiledClasses() throws Exception {
      */
     @AfterAll
     static void reportFootprint() {
-        final long library = bytesOf(closureOf("XmlFactories"));
+        final long library = bytesOf(rootClosure());
         final StringBuilder report = new StringBuilder("\nShade footprint 
(uncompressed .class bytes, % of full library):\n");
         for (final String entry : REPORTED) {
             final Set<String> closure = closureOf(entry);
@@ -158,6 +158,10 @@ static void reportFootprint() {
         }
     }
 
+    private static Set<String> rootClosure() {
+        return closureOf("HardeningDocumentBuilderFactory");
+    }
+
     /**
      * {@link #SAX_PARSER_HARDENER} plus the extra names; used where an entry 
point's closure is the SAX path plus its own classes.
      */
@@ -182,7 +186,7 @@ void documentBuilderHardenerFootprint() {
 
     @Test
     void onlyXmlFactoriesPullsTheWholeLibrary() {
-        assertEquals(LIBRARY_CLASS_COUNT, closureOf("XmlFactories").size(), 
"XmlFactories closure size drifted");
+        assertEquals(LIBRARY_CLASS_COUNT, rootClosure().size(), "XML factories 
closure size drifted");
     }
 
     @Test
diff --git a/src/test/java/org/apache/commons/xml/XIncludeTest.java 
b/src/test/java/org/apache/commons/xml/XIncludeTest.java
index 06ea446..3dbc4ce 100644
--- a/src/test/java/org/apache/commons/xml/XIncludeTest.java
+++ b/src/test/java/org/apache/commons/xml/XIncludeTest.java
@@ -43,7 +43,7 @@
 import org.xml.sax.XMLReader;
 
 /**
- * Tests that XInclude resolution is blocked by default on factories from 
{@link XmlFactories}, and that callers can
+ * Tests that XInclude resolution is blocked by default on factories from 
{@link org.apache.commons.xml}, and that callers can
  * allow-list specific resources via an {@link EntityResolver}.
  *
  * <p>Each case is exercised in both {@code parse="xml"} and {@code 
parse="text"} modes, and for both DOM and SAX
@@ -187,7 +187,7 @@ void baselineSaxLeaksParseXml() throws Exception {
     void hardenedDomBlocksParseText() throws Exception {
         final InputSource input = inputSource(xiIncludeXml(REFERENCED_TEXT, 
"text"));
 
-        final DocumentBuilderFactory factory = 
XmlFactories.newDocumentBuilderFactory();
+        final DocumentBuilderFactory factory = 
HardeningDocumentBuilderFactory.newInstance();
         factory.setNamespaceAware(true);
         assumeXIncludeAware(factory);
         final Document doc = factory.newDocumentBuilder().parse(input);
@@ -201,7 +201,7 @@ void hardenedDomBlocksParseText() throws Exception {
     void hardenedDomBlocksParseXml() throws Exception {
         final InputSource input = inputSource(xiIncludeXml(REFERENCED_XML, 
"xml"));
 
-        final DocumentBuilderFactory factory = 
XmlFactories.newDocumentBuilderFactory();
+        final DocumentBuilderFactory factory = 
HardeningDocumentBuilderFactory.newInstance();
         factory.setNamespaceAware(true);
         assumeXIncludeAware(factory);
         assertThrows(SAXException.class, () -> {
@@ -215,7 +215,7 @@ void hardenedDomBlocksParseXml() throws Exception {
     void hardenedDomNullResolverDoesNotLeak() throws Exception {
         final InputSource input = inputSource(xiIncludeXml(REFERENCED_XML, 
"xml"));
 
-        final DocumentBuilderFactory factory = 
XmlFactories.newDocumentBuilderFactory();
+        final DocumentBuilderFactory factory = 
HardeningDocumentBuilderFactory.newInstance();
         factory.setNamespaceAware(true);
         assumeXIncludeAware(factory);
         final DocumentBuilder builder = factory.newDocumentBuilder();
@@ -229,7 +229,7 @@ void hardenedDomNullResolverDoesNotLeak() throws Exception {
     void hardenedDomWithAllowListResolvesParseText() throws Exception {
         final InputSource input = inputSource(xiIncludeXml(REFERENCED_TEXT, 
"text"));
 
-        final DocumentBuilderFactory factory = 
XmlFactories.newDocumentBuilderFactory();
+        final DocumentBuilderFactory factory = 
HardeningDocumentBuilderFactory.newInstance();
         factory.setNamespaceAware(true);
         assumeXIncludeAware(factory);
         final DocumentBuilder builder = factory.newDocumentBuilder();
@@ -248,7 +248,7 @@ void hardenedDomWithAllowListResolvesParseText() throws 
Exception {
     void hardenedDomWithAllowListResolvesParseXml() throws Exception {
         final InputSource input = inputSource(xiIncludeXml(REFERENCED_XML, 
"xml"));
 
-        final DocumentBuilderFactory factory = 
XmlFactories.newDocumentBuilderFactory();
+        final DocumentBuilderFactory factory = 
HardeningDocumentBuilderFactory.newInstance();
         factory.setNamespaceAware(true);
         assumeXIncludeAware(factory);
         final DocumentBuilder builder = factory.newDocumentBuilder();
@@ -263,7 +263,7 @@ void hardenedDomWithAllowListResolvesParseXml() throws 
Exception {
     void hardenedSaxBlocksParseText() throws Exception {
         final String input = xiIncludeXml(REFERENCED_TEXT, "text");
 
-        final SAXParserFactory factory = XmlFactories.newSAXParserFactory();
+        final SAXParserFactory factory = 
HardeningSAXParserFactory.newInstance();
         factory.setNamespaceAware(true);
         assumeXIncludeAware(factory);
         final String captured = 
captureCharacters(factory.newSAXParser().getXMLReader(), input);
@@ -276,7 +276,7 @@ void hardenedSaxBlocksParseText() throws Exception {
     void hardenedSaxBlocksParseXml() throws Exception {
         final InputSource input = inputSource(xiIncludeXml(REFERENCED_XML, 
"xml"));
 
-        final SAXParserFactory factory = XmlFactories.newSAXParserFactory();
+        final SAXParserFactory factory = 
HardeningSAXParserFactory.newInstance();
         factory.setNamespaceAware(true);
         assumeXIncludeAware(factory);
         assertThrows(SAXException.class, () -> {
@@ -290,7 +290,7 @@ void hardenedSaxBlocksParseXml() throws Exception {
     void hardenedSaxNullResolverDoesNotLeak() throws Exception {
         final InputSource input = inputSource(xiIncludeXml(REFERENCED_XML, 
"xml"));
 
-        final SAXParserFactory factory = XmlFactories.newSAXParserFactory();
+        final SAXParserFactory factory = 
HardeningSAXParserFactory.newInstance();
         factory.setNamespaceAware(true);
         assumeXIncludeAware(factory);
         final XMLReader reader = factory.newSAXParser().getXMLReader();
@@ -304,7 +304,7 @@ void hardenedSaxNullResolverDoesNotLeak() throws Exception {
     void hardenedSaxWithAllowListResolvesParseText() throws Exception {
         final String input = xiIncludeXml(REFERENCED_TEXT, "text");
 
-        final SAXParserFactory factory = XmlFactories.newSAXParserFactory();
+        final SAXParserFactory factory = 
HardeningSAXParserFactory.newInstance();
         factory.setNamespaceAware(true);
         assumeXIncludeAware(factory);
         final XMLReader reader = factory.newSAXParser().getXMLReader();
@@ -319,7 +319,7 @@ void hardenedSaxWithAllowListResolvesParseText() throws 
Exception {
     void hardenedSaxWithAllowListResolvesParseXml() throws Exception {
         final String input = xiIncludeXml(REFERENCED_XML, "xml");
 
-        final SAXParserFactory factory = XmlFactories.newSAXParserFactory();
+        final SAXParserFactory factory = 
HardeningSAXParserFactory.newInstance();
         factory.setNamespaceAware(true);
         assumeXIncludeAware(factory);
         final XMLReader reader = factory.newSAXParser().getXMLReader();
diff --git a/src/test/java/org/apache/commons/xml/XMLFilterParseStringTest.java 
b/src/test/java/org/apache/commons/xml/XMLFilterParseStringTest.java
index e7a2a98..8b0b98b 100644
--- a/src/test/java/org/apache/commons/xml/XMLFilterParseStringTest.java
+++ b/src/test/java/org/apache/commons/xml/XMLFilterParseStringTest.java
@@ -53,7 +53,7 @@ private static String entityPayload() {
 
     @Test
     void hardenedFilterParseStringDoesNotLeakExternalEntity(@TempDir final 
Path tmpDir) throws Exception {
-        final SAXTransformerFactory factory = (SAXTransformerFactory) 
XmlFactories.newTransformerFactory();
+        final SAXTransformerFactory factory = (SAXTransformerFactory) 
HardeningTransformerFactory.newInstance();
         final Templates templates = factory.newTemplates(new StreamSource(new 
StringReader(IDENTITY_XSLT)));
         final XMLFilter filter = factory.newXMLFilter(templates);
         final Path tmp = Files.createTempFile(tmpDir, "xmlfilter", ".xml");
diff --git a/src/test/java/org/apache/commons/xml/XPathInputSourceTest.java 
b/src/test/java/org/apache/commons/xml/XPathInputSourceTest.java
index e28acd3..0b7a809 100644
--- a/src/test/java/org/apache/commons/xml/XPathInputSourceTest.java
+++ b/src/test/java/org/apache/commons/xml/XPathInputSourceTest.java
@@ -52,14 +52,14 @@ private static String entityPayload() {
     void hardenedXPathEvaluateDoesNotLeak() throws Exception {
         // Deterministic on every engine: the entity is declared in the 
internal subset and the floor resolves only its
         // external content — to empty replacement text — so the pre-parse 
completes and the reference expands to nothing.
-        final String result = 
XmlFactories.newXPathFactory().newXPath().evaluate(EXPRESSION, 
AttackTestSupport.inputSource(entityPayload()));
+        final String result = 
HardeningXPathFactory.newXPathFactory().newXPath().evaluate(EXPRESSION, 
AttackTestSupport.inputSource(entityPayload()));
         assertFalse(result.contains(AttackTestSupport.LEAKED_MARKER), 
"external entity leaked into the XPath result: " + result);
     }
 
     @Test
     void hardenedXPathEvaluatesPlainDocument() throws Exception {
         // Positive control: the hardened pre-parse still evaluates an 
entity-free document end to end.
-        final String result = 
XmlFactories.newXPathFactory().newXPath().evaluate(EXPRESSION,
+        final String result = 
HardeningXPathFactory.newXPathFactory().newXPath().evaluate(EXPRESSION,
                 AttackTestSupport.inputSource(AttackTestSupport.xmlBody("plain 
text")));
         assertEquals("plain text", result, "hardened XPath should evaluate a 
plain document");
     }
@@ -67,7 +67,7 @@ void hardenedXPathEvaluatesPlainDocument() throws Exception {
     @Test
     void hardenedXPathExpressionEvaluateDoesNotLeak() throws Exception {
         // Same declared-entity outcome as above on the compiled-expression 
entry point.
-        final String result = 
XmlFactories.newXPathFactory().newXPath().compile(EXPRESSION).evaluate(AttackTestSupport.inputSource(entityPayload()));
+        final String result = 
HardeningXPathFactory.newXPathFactory().newXPath().compile(EXPRESSION).evaluate(AttackTestSupport.inputSource(entityPayload()));
         assertFalse(result.contains(AttackTestSupport.LEAKED_MARKER), 
"external entity leaked into the compiled XPath result: " + result);
     }
 

Reply via email to