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

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


The following commit(s) were added to refs/heads/main by this push:
     new 0d884ad  Reject a foreign Templates in newTransformerHandler in the 
TrAX shape (#94)
0d884ad is described below

commit 0d884ad8ca5edac14584baa13f93ea5180025bc3
Author: Piotr P. Karwasz <[email protected]>
AuthorDate: Mon Sep 14 00:01:26 2026 +0200

    Reject a foreign Templates in newTransformerHandler in the TrAX shape (#94)
    
    * Reject a foreign Templates in newTransformerHandler in the TrAX shape.
    
    An implementation builds a TransformerHandler by casting the Templates, or
    the Transformer it produces, to its own type, so one it did not compile
    cannot be used at all. Apache Xalan and the stock JDK signal that with an
    undeclared ClassCastException; report it as the
    TransformerConfigurationException Saxon already raises for it.
    
    Assisted-By: Claude Opus 5 (1M context) <[email protected]>
    Claude-Session: https://claude.ai/code/session_01AaDyR9HjZLWkFn42x7kje3
    
    * Improve assertion for foreign Templates handling
    
    Co-authored-by: Copilot Autofix powered by AI 
<[email protected]>
    
    * Keep the failure message free of interpretation.
    
    A ClassCastException out of the delegate need not mean the Templates was
    refused for being foreign, so the message now states only what was attempted
    and with what, and leaves the cause to say the rest.
    
    The assertion follows: it no longer pins the whole message, and it asks for
    the cause and the named Templates only where the ClassCastException actually
    escaped. Saxon reports the refusal itself, in its own words and without that
    cause.
    
    Assisted-By: Claude Opus 5 (1M context) <[email protected]>
    Claude-Session: https://claude.ai/code/session_01AaDyR9HjZLWkFn42x7kje3
    
    * Reject a null Templates the same way on every implementation.
    
    newTransformerHandler(Templates) raised NullPointerException everywhere but
    on Saxon, which reported the null as a TransformerConfigurationException.
    Rejecting the argument up front makes that check independent of the
    implementation, as it already is on the other creation methods, and a test
    now pins null handling across the whole creation surface.
    
    The ClassCastException translation cannot meet a null: casting one never
    throws, so the message never reads the class of a null Templates.
    
    Assisted-By: Claude Opus 5 (1M context) <[email protected]>
    Claude-Session: https://claude.ai/code/session_01AaDyR9HjZLWkFn42x7kje3
    
    ---------
    
    Co-authored-by: Gary Gregory <[email protected]>
    Co-authored-by: Copilot Autofix powered by AI 
<[email protected]>
---
 src/changes/changes.xml                            |  1 +
 .../xml/secure/SecureTransformerFactory.java       | 18 ++++++++--
 .../xml/secure/SecureTransformerFactoryTest.java   | 39 ++++++++++++++++++++++
 3 files changed, 56 insertions(+), 2 deletions(-)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 708c972..cbe6d71 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -35,6 +35,7 @@ The <action> type attribute can be add, update, fix, or 
remove.
       <!-- FIX -->
       <action type="fix" dev="ggregory" due-to="Gary Gregory">Fix the 
OpenRewrite migration recipe to target static method calls instead of class 
references.</action>
       <action type="fix" dev="ggregory" due-to="Gary Gregory">Fix the 
OpenRewrite migration recipe to add a dependency on 
org.apache.commons:commons-secure-xml:1.0.0.</action>
+      <action type="fix" dev="pkarwasz" due-to="Piotr P. Karwasz, Gary 
Gregory">Fix rejection behavior of foreign Templates in 
SAXTransformerFactory.newTransformerHandler.</action>
       <action type="fix" dev="pkarwasz" due-to="Piotr P. Karwasz, Gary 
Gregory">Fix a NullPointerException when an XMLFilter with a self-driven parent 
reader is parsed with a null InputSource. #86</action>
       <!-- ADD -->
       <!-- UPDATE -->
diff --git 
a/src/main/java/org/apache/commons/xml/secure/SecureTransformerFactory.java 
b/src/main/java/org/apache/commons/xml/secure/SecureTransformerFactory.java
index b55a68f..a609e1e 100644
--- a/src/main/java/org/apache/commons/xml/secure/SecureTransformerFactory.java
+++ b/src/main/java/org/apache/commons/xml/secure/SecureTransformerFactory.java
@@ -308,10 +308,24 @@ public TransformerHandler newTransformerHandler(final 
Source source) throws Tran
             return 
secure(delegate.newTransformerHandler(SecureSAXParserFactory.secure(source, 
overrideDefaultParser())));
         }
 
+        /**
+         * {@inheritDoc}
+         *
+         * <p>Most implementations reject a {@link Templates} they did not 
compile, some in the TrAX shape, others by casting it or its Transformer to 
their
+         * own type. Both reach the caller as a {@link 
TransformerConfigurationException}.</p>
+         */
         @Override
         public TransformerHandler newTransformerHandler(final Templates 
templates) throws TransformerConfigurationException {
-            // Implementations cast templates.newTransformer() to their own 
Transformer type, so hand them the wrapped implementation Templates, not the 
wrapper.
-            return secure(delegate.newTransformerHandler(unwrap(templates)));
+            // Implementations:
+            // - cast templates.newTransformer() to their own Transformer 
type, so hand them the wrapped implementation Templates, not the wrapper;
+            // - raise NullPointerException for a null argument, except Saxon; 
rejecting it here keeps that uniform, as on the other methods.
+            final Templates unwrapped = 
unwrap(Objects.requireNonNull(templates, "templates"));
+            try {
+                return secure(delegate.newTransformerHandler(unwrapped));
+            } catch (final ClassCastException e) {
+                throw new TransformerConfigurationException("Failed to create 
a TransformerHandler from a Templates of type "
+                        + unwrapped.getClass().getName(), e);
+            }
         }
 
         /**
diff --git 
a/src/test/java/org/apache/commons/xml/secure/SecureTransformerFactoryTest.java 
b/src/test/java/org/apache/commons/xml/secure/SecureTransformerFactoryTest.java
index 1c309b8..3c81f76 100644
--- 
a/src/test/java/org/apache/commons/xml/secure/SecureTransformerFactoryTest.java
+++ 
b/src/test/java/org/apache/commons/xml/secure/SecureTransformerFactoryTest.java
@@ -22,10 +22,12 @@
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertSame;
 import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.io.StringReader;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Properties;
 
 import javax.xml.XMLConstants;
 import javax.xml.parsers.DocumentBuilderFactory;
@@ -192,6 +194,43 @@ void rejectsDelegatesThatCannotEnableSecureProcessing() {
         assertThrows(SecureException.class, () -> 
SecureTransformerFactory.secure(new RejectingFeatureFactory()));
     }
 
+    @Test
+    void rejectsNullInputOnEveryFactoryMethod() {
+        final SAXTransformerFactory factory = (SAXTransformerFactory) 
SecureTransformerFactory.newInstance();
+        assertThrows(NullPointerException.class, () -> 
factory.newTemplates(null));
+        assertThrows(NullPointerException.class, () -> 
factory.newTransformer(null));
+        assertThrows(NullPointerException.class, () -> 
factory.newTransformerHandler((Source) null));
+        assertThrows(NullPointerException.class, () -> 
factory.newTransformerHandler((Templates) null));
+        assertThrows(NullPointerException.class, () -> 
factory.newXMLFilter((Source) null));
+        assertThrows(NullPointerException.class, () -> 
factory.newXMLFilter((Templates) null));
+    }
+
+    @Test
+    void rejectsForeignTemplatesFromNewTransformerHandler() throws Exception {
+        final SAXTransformerFactory factory = (SAXTransformerFactory) 
SecureTransformerFactory.newInstance();
+        final Templates own = factory.newTemplates(stylesheet());
+        // A caller's own Templates wrapper, the shape a framework uses to 
carry parameters onto the Transformer it hands out.
+        final Templates foreign = new Templates() {
+
+            @Override
+            public Properties getOutputProperties() {
+                return own.getOutputProperties();
+            }
+
+            @Override
+            public Transformer newTransformer() throws 
TransformerConfigurationException {
+                return own.newTransformer();
+            }
+        };
+        // Xalan and XSLTC reject a Templates they did not compile with an 
undeclared ClassCastException, where Saxon uses the TrAX shape.
+        final TransformerConfigurationException exception = 
assertThrows(TransformerConfigurationException.class,
+                () -> factory.newTransformerHandler(foreign));
+        // Saxon reports it in its own words; where the ClassCastException 
escaped instead, it must arrive as the cause, under a message naming the 
Templates.
+        if (exception.getCause() instanceof ClassCastException) {
+            
assertTrue(exception.getMessage().contains(foreign.getClass().getName()), 
exception.getMessage());
+        }
+    }
+
     @Test
     void securesAssociatedStylesheetSourcesOfEverySupportedShape() throws 
Exception {
         final SAXTransformerFactory factory = (SAXTransformerFactory) 
SecureTransformerFactory.newInstance();

Reply via email to