You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2026/07/16 13:39:09 UTC

(camel) branch main updated: CAMEL-24108: camel-xslt-saxon - apply secureProcessing unconditionally (#24777)

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

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new 48273f53b93d CAMEL-24108: camel-xslt-saxon - apply secureProcessing unconditionally (#24777)
48273f53b93d is described below

commit 48273f53b93d303a183f175d859bab312ef6adfe
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Thu Jul 16 15:39:03 2026 +0200

    CAMEL-24108: camel-xslt-saxon - apply secureProcessing unconditionally (#24777)
    
    Co-Authored-By: Claude Opus 4.6 <no...@anthropic.com>
---
 .../component/xslt/saxon/XsltSaxonEndpoint.java    | 38 +++++++----
 .../component/xslt/saxon/XsltSaxonHelper.java      |  9 ++-
 .../saxon/XsltSaxonConfigurationStrategyTest.java  | 64 +++++++++++++++++++
 .../xslt/saxon/XsltSaxonSecureProcessingTest.java  | 74 ++++++++++++++++++++++
 .../apache/camel/component/xslt/XsltEndpoint.java  | 14 ++--
 .../ROOT/pages/camel-4x-upgrade-guide-4_22.adoc    | 19 ++++++
 6 files changed, 196 insertions(+), 22 deletions(-)

diff --git a/components/camel-xslt-saxon/src/main/java/org/apache/camel/component/xslt/saxon/XsltSaxonEndpoint.java b/components/camel-xslt-saxon/src/main/java/org/apache/camel/component/xslt/saxon/XsltSaxonEndpoint.java
index 87b3fed7a42a..4b2e0cd44cd3 100644
--- a/components/camel-xslt-saxon/src/main/java/org/apache/camel/component/xslt/saxon/XsltSaxonEndpoint.java
+++ b/components/camel-xslt-saxon/src/main/java/org/apache/camel/component/xslt/saxon/XsltSaxonEndpoint.java
@@ -24,6 +24,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import javax.xml.XMLConstants;
 import javax.xml.parsers.ParserConfigurationException;
 import javax.xml.parsers.SAXParser;
 import javax.xml.parsers.SAXParserFactory;
@@ -45,6 +46,7 @@ import org.apache.camel.Component;
 import org.apache.camel.Exchange;
 import org.apache.camel.api.management.ManagedAttribute;
 import org.apache.camel.api.management.ManagedResource;
+import org.apache.camel.component.xslt.TransformerFactoryConfigurationStrategy;
 import org.apache.camel.component.xslt.XsltBuilder;
 import org.apache.camel.component.xslt.XsltEndpoint;
 import org.apache.camel.spi.ClassResolver;
@@ -225,28 +227,40 @@ public class XsltSaxonEndpoint extends XsltEndpoint {
         TransformerFactory factory = getTransformerFactory();
         if (factory == null) {
             if (getTransformerFactoryClass() == null) {
-                // create new saxon factory
                 factory = new TransformerFactoryImpl();
             } else {
-                // provide the class loader of this component to work in OSGi environments
                 Class<TransformerFactory> factoryClass = resolver.resolveMandatoryClass(getTransformerFactoryClass(),
                         TransformerFactory.class, XsltSaxonComponent.class.getClassLoader());
                 LOG.debug("Using TransformerFactoryClass {}", factoryClass);
                 factory = injector.newInstance(factoryClass);
             }
-        }
 
-        if (factory instanceof TransformerFactoryImpl) {
-            TransformerFactoryImpl tf = (TransformerFactoryImpl) factory;
-            XsltSaxonHelper.registerSaxonConfiguration(tf, saxonConfiguration);
-            XsltSaxonHelper.registerSaxonConfigurationProperties(tf, saxonConfigurationProperties);
-            XsltSaxonHelper.registerSaxonExtensionFunctions(tf, saxonExtensionFunctions, secureProcessing);
-        }
+            if (factory instanceof TransformerFactoryImpl tf) {
+                XsltSaxonHelper.configureSecureProcessing(tf, secureProcessing);
+                XsltSaxonHelper.registerSaxonConfiguration(tf, saxonConfiguration);
+                XsltSaxonHelper.registerSaxonConfigurationProperties(tf, saxonConfigurationProperties);
+                XsltSaxonHelper.registerSaxonExtensionFunctions(tf, saxonExtensionFunctions);
+            }
+
+            try {
+                factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
+            } catch (IllegalArgumentException e) {
+                LOG.debug("TransformerFactory does not support {}", XMLConstants.ACCESS_EXTERNAL_DTD);
+            }
+            try {
+                factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
+            } catch (IllegalArgumentException e) {
+                LOG.debug("TransformerFactory does not support {}", XMLConstants.ACCESS_EXTERNAL_STYLESHEET);
+            }
 
-        if (factory != null) {
-            LOG.debug("Using TransformerFactory {}", factory);
-            xslt.setTransformerFactory(factory);
+            final TransformerFactoryConfigurationStrategy strategy = getTransformerFactoryConfigurationStrategy();
+            if (strategy != null) {
+                strategy.configure(factory, this);
+            }
         }
+
+        LOG.debug("Using TransformerFactory {}", factory);
+        xslt.setTransformerFactory(factory);
         if (getResultHandlerFactory() != null) {
             xslt.setResultHandlerFactory(getResultHandlerFactory());
         }
diff --git a/components/camel-xslt-saxon/src/main/java/org/apache/camel/component/xslt/saxon/XsltSaxonHelper.java b/components/camel-xslt-saxon/src/main/java/org/apache/camel/component/xslt/saxon/XsltSaxonHelper.java
index 0a6f3be451f9..40e9ca881bba 100644
--- a/components/camel-xslt-saxon/src/main/java/org/apache/camel/component/xslt/saxon/XsltSaxonHelper.java
+++ b/components/camel-xslt-saxon/src/main/java/org/apache/camel/component/xslt/saxon/XsltSaxonHelper.java
@@ -50,13 +50,16 @@ final class XsltSaxonHelper {
         }
     }
 
+    public static void configureSecureProcessing(TransformerFactoryImpl factory, boolean secureProcessing)
+            throws Exception {
+        factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, secureProcessing);
+    }
+
     public static void registerSaxonExtensionFunctions(
             TransformerFactoryImpl factory,
-            List<Object> saxonExtensionFunctions,
-            boolean secureProcessing)
+            List<Object> saxonExtensionFunctions)
             throws Exception {
         if (saxonExtensionFunctions != null && !saxonExtensionFunctions.isEmpty()) {
-            factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, secureProcessing);
             for (Object extensionFunction : saxonExtensionFunctions) {
                 if (extensionFunction instanceof ExtensionFunctionDefinition) {
                     LOG.debug("Saxon.registerExtensionFunction {}", extensionFunction);
diff --git a/components/camel-xslt-saxon/src/test/java/org/apache/camel/component/xslt/saxon/XsltSaxonConfigurationStrategyTest.java b/components/camel-xslt-saxon/src/test/java/org/apache/camel/component/xslt/saxon/XsltSaxonConfigurationStrategyTest.java
new file mode 100644
index 000000000000..4a14f6a70361
--- /dev/null
+++ b/components/camel-xslt-saxon/src/test/java/org/apache/camel/component/xslt/saxon/XsltSaxonConfigurationStrategyTest.java
@@ -0,0 +1,64 @@
+/*
+ * 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
+ *
+ *      http://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.camel.component.xslt.saxon;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.xml.transform.TransformerFactory;
+
+import org.apache.camel.BindToRegistry;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.xslt.TransformerFactoryConfigurationStrategy;
+import org.apache.camel.component.xslt.XsltEndpoint;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class XsltSaxonConfigurationStrategyTest extends CamelTestSupport {
+
+    private final AtomicBoolean strategyCalled = new AtomicBoolean();
+
+    @BindToRegistry("myStrategy")
+    private final TransformerFactoryConfigurationStrategy strategy = new TransformerFactoryConfigurationStrategy() {
+        @Override
+        public void configure(TransformerFactory factory, XsltEndpoint endpoint) {
+            strategyCalled.set(true);
+        }
+    };
+
+    @Test
+    public void testConfigurationStrategyIsInvoked() throws Exception {
+        getMockEndpoint("mock:result").expectedMessageCount(1);
+        template.sendBody("direct:start", "<mail><subject>Hey</subject><body>Hello world!</body></mail>");
+        getMockEndpoint("mock:result").assertIsSatisfied();
+
+        assertTrue(strategyCalled.get(), "TransformerFactoryConfigurationStrategy should have been invoked");
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("direct:start")
+                        .to("xslt-saxon:xslt/transform.xsl?transformerFactoryConfigurationStrategy=#myStrategy")
+                        .to("mock:result");
+            }
+        };
+    }
+}
diff --git a/components/camel-xslt-saxon/src/test/java/org/apache/camel/component/xslt/saxon/XsltSaxonSecureProcessingTest.java b/components/camel-xslt-saxon/src/test/java/org/apache/camel/component/xslt/saxon/XsltSaxonSecureProcessingTest.java
new file mode 100644
index 000000000000..c23a2bd15484
--- /dev/null
+++ b/components/camel-xslt-saxon/src/test/java/org/apache/camel/component/xslt/saxon/XsltSaxonSecureProcessingTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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
+ *
+ *      http://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.camel.component.xslt.saxon;
+
+import javax.xml.XMLConstants;
+
+import net.sf.saxon.TransformerFactoryImpl;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class XsltSaxonSecureProcessingTest extends CamelTestSupport {
+
+    @Test
+    public void testSecureProcessingEnabledByDefault() throws Exception {
+        XsltSaxonEndpoint endpoint
+                = context.getEndpoint("xslt-saxon:xslt/transform.xsl", XsltSaxonEndpoint.class);
+        assertTrue(endpoint.isSecureProcessing());
+
+        TransformerFactoryImpl factory = new TransformerFactoryImpl();
+        XsltSaxonHelper.configureSecureProcessing(factory, true);
+        assertTrue(factory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING));
+    }
+
+    @Test
+    public void testSecureProcessingCanBeDisabled() throws Exception {
+        XsltSaxonEndpoint endpoint
+                = context.getEndpoint("xslt-saxon:xslt/transform.xsl?secureProcessing=false", XsltSaxonEndpoint.class);
+        assertFalse(endpoint.isSecureProcessing());
+
+        TransformerFactoryImpl factory = new TransformerFactoryImpl();
+        XsltSaxonHelper.configureSecureProcessing(factory, false);
+        assertFalse(factory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING));
+    }
+
+    @Test
+    public void testTransformWithSecureProcessing() throws Exception {
+        getMockEndpoint("mock:result").expectedMessageCount(1);
+        template.sendBody("direct:default", "<mail><subject>Hey</subject><body>Hello world!</body></mail>");
+        getMockEndpoint("mock:result").assertIsSatisfied();
+
+        String xml = getMockEndpoint("mock:result").getReceivedExchanges().get(0).getIn().getBody(String.class);
+        assertTrue(xml.contains("transformed"));
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("direct:default")
+                        .to("xslt-saxon:xslt/transform.xsl")
+                        .to("mock:result");
+            }
+        };
+    }
+}
diff --git a/components/camel-xslt/src/main/java/org/apache/camel/component/xslt/XsltEndpoint.java b/components/camel-xslt/src/main/java/org/apache/camel/component/xslt/XsltEndpoint.java
index 9e6500882af1..d074b26085a2 100644
--- a/components/camel-xslt/src/main/java/org/apache/camel/component/xslt/XsltEndpoint.java
+++ b/components/camel-xslt/src/main/java/org/apache/camel/component/xslt/XsltEndpoint.java
@@ -451,17 +451,17 @@ public class XsltEndpoint extends ProcessorEndpoint {
                         TransformerFactory.class, XsltComponent.class.getClassLoader());
                 LOG.debug("Using TransformerFactoryClass {}", factoryClass);
                 factory = injector.newInstance(factoryClass);
-
-                final TransformerFactoryConfigurationStrategy tfConfigStrategy = transformerFactoryConfigurationStrategy != null
-                        ? transformerFactoryConfigurationStrategy
-                        : ((XsltComponent) getComponent()).getTransformerFactoryConfigurationStrategy();
-                if (tfConfigStrategy != null) {
-                    tfConfigStrategy.configure(factory, this);
-                }
             }
         }
 
         if (factory != null) {
+            final TransformerFactoryConfigurationStrategy tfConfigStrategy = transformerFactoryConfigurationStrategy != null
+                    ? transformerFactoryConfigurationStrategy
+                    : ((XsltComponent) getComponent()).getTransformerFactoryConfigurationStrategy();
+            if (tfConfigStrategy != null) {
+                tfConfigStrategy.configure(factory, this);
+            }
+
             LOG.debug("Using TransformerFactory {}", factory);
             xslt.setTransformerFactory(factory);
         }
diff --git a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc
index dde8b9d61593..86c29ff5905b 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc
@@ -525,3 +525,22 @@ Two behavior changes follow:
   not a per-poll cap; set it lower to reduce page sizes.
 * On startup the consumer tails events from the moment it starts, rather than first replaying the
   single most-recent historical event.
+
+=== camel-xslt-saxon - secure processing now applied unconditionally
+
+The `secureProcessing` option (default `true`) is now applied to the Saxon `TransformerFactory`
+unconditionally. Previously it was only set when `saxonExtensionFunctions` was configured,
+meaning the default configuration silently ran without `FEATURE_SECURE_PROCESSING`.
+
+Additionally, the Saxon factory now sets `ACCESS_EXTERNAL_DTD` and `ACCESS_EXTERNAL_STYLESHEET`
+to empty strings (matching the behavior of the plain `xslt` component), restricting
+stylesheet-driven external fetches.
+
+Users of Saxon Professional or Enterprise editions who rely on Java extension functions
+called from XSLT stylesheets must now explicitly set `secureProcessing=false` on the endpoint.
+
+=== camel-xslt / camel-xslt-saxon - transformerFactoryConfigurationStrategy now honored
+
+The `transformerFactoryConfigurationStrategy` option is now applied on all factory creation paths.
+Previously on `xslt-saxon` it was never invoked, and on plain `xslt` it was only invoked when
+`transformerFactoryClass` was explicitly set.