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 <[email protected]>
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 <[email protected]>
---
.../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.