This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch fix/CAMEL-24108 in repository https://gitbox.apache.org/repos/asf/camel.git
commit 1b7a4081895a67598f9dfc032f6096185ada82ab Author: Claus Ibsen <[email protected]> AuthorDate: Thu Jul 16 11:56:54 2026 +0200 CAMEL-24108: camel-xslt-saxon - apply secureProcessing unconditionally and honor transformerFactoryConfigurationStrategy Co-Authored-By: Claude Opus 4.6 <[email protected]> Signed-off-by: Claus Ibsen <[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 80753018c84a..c8c5292e72e8 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 @@ -514,3 +514,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.
