This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch fix/CAMEL-24110 in repository https://gitbox.apache.org/repos/asf/camel.git
commit e6a2ad8b4c3147c1b45042b2bd2adf6fe42fa8fc Author: Claus Ibsen <[email protected]> AuthorDate: Thu Jul 16 12:07:10 2026 +0200 CAMEL-24110: camel-xslt-saxon - fix saxonReaderProperties null-guard always true, DOMSource from URIResolver broken, unhardened SAXParser Co-Authored-By: Claude Opus 4.6 <[email protected]> Signed-off-by: Claus Ibsen <[email protected]> --- .../component/xslt/saxon/XsltSaxonEndpoint.java | 30 +++---- .../saxon/XsltSaxonUriResolverDomSourceTest.java | 92 ++++++++++++++++++++++ 2 files changed, 108 insertions(+), 14 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..59157511c53d 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; @@ -282,9 +283,7 @@ public class XsltSaxonEndpoint extends XsltEndpoint { InputStream is = getCamelContext().getTypeConverter().mandatoryConvertTo(InputStream.class, exchange, template); XsltBuilder builder = createXsltBuilder(); Source source = new StreamSource(is); - if (this.saxonReaderProperties != null) { - //for Saxon we need to create XMLReader for the coming source - //so that the features configuration can take effect + if (!this.saxonReaderProperties.isEmpty()) { source = createReaderForSource(source); } builder.setTransformerSource(source); @@ -302,23 +301,27 @@ public class XsltSaxonEndpoint extends XsltEndpoint { protected void loadResource(String resourceUri, XsltBuilder xslt) throws TransformerException, IOException { LOG.trace("{} loading schema resource: {}", this, resourceUri); Source source = xslt.getUriResolver().resolve(resourceUri, null); - if (this.saxonReaderProperties != null) { - //for Saxon we need to create XMLReader for the coming source - //so that the features configuration can take effect - source = createReaderForSource(source); - } if (source == null) { throw new IOException("Cannot load schema resource " + resourceUri); - } else { - xslt.setTransformerSource(source); } - // now loaded so clear flag + if (!this.saxonReaderProperties.isEmpty()) { + source = createReaderForSource(source); + } + xslt.setTransformerSource(source); setCacheCleared(false); } private Source createReaderForSource(Source source) { + InputSource inputSource = SAXSource.sourceToInputSource(source); + if (inputSource == null) { + return source; + } try { SAXParserFactory factory = SAXParserFactory.newInstance(); + factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + factory.setFeature("http://xml.org/sax/features/external-general-entities", false); + factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); SAXParser saxParser = factory.newSAXParser(); XMLReader xmlReader = saxParser.getXMLReader(); @@ -329,7 +332,7 @@ public class XsltSaxonEndpoint extends XsltEndpoint { URI uri = new URI(key); if (value != null && (value.toString().equals("true") || (value.toString().equals("false")))) { - xmlReader.setFeature(uri.toString(), Boolean.valueOf(value.toString())); + xmlReader.setFeature(uri.toString(), Boolean.parseBoolean(value.toString())); } else if (value != null) { xmlReader.setProperty(uri.toString(), value); } @@ -337,11 +340,10 @@ public class XsltSaxonEndpoint extends XsltEndpoint { LOG.debug("{} isn't a valid URI, so ignore it", key); } } - InputSource inputSource = SAXSource.sourceToInputSource(source); return new SAXSource(xmlReader, inputSource); } catch (SAXException | ParserConfigurationException e) { LOG.info("Can't create XMLReader for source ", e); - return null; + return source; } } diff --git a/components/camel-xslt-saxon/src/test/java/org/apache/camel/component/xslt/saxon/XsltSaxonUriResolverDomSourceTest.java b/components/camel-xslt-saxon/src/test/java/org/apache/camel/component/xslt/saxon/XsltSaxonUriResolverDomSourceTest.java new file mode 100644 index 000000000000..d147735b0025 --- /dev/null +++ b/components/camel-xslt-saxon/src/test/java/org/apache/camel/component/xslt/saxon/XsltSaxonUriResolverDomSourceTest.java @@ -0,0 +1,92 @@ +/* + * 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.io.StringReader; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.transform.Source; +import javax.xml.transform.TransformerException; +import javax.xml.transform.URIResolver; +import javax.xml.transform.dom.DOMSource; + +import org.w3c.dom.Document; + +import org.xml.sax.InputSource; + +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.spi.Registry; +import org.apache.camel.test.junit6.CamelTestSupport; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class XsltSaxonUriResolverDomSourceTest extends CamelTestSupport { + + private static final String XSL = "<?xml version='1.0'?>" + + "<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='2.0'>" + + " <xsl:output method='xml' indent='yes'/>" + + " <xsl:template match='/'>" + + " <transformed><xsl:copy-of select='*'/></transformed>" + + " </xsl:template>" + + "</xsl:stylesheet>"; + + @Test + public void testUriResolverReturningDomSource() throws Exception { + MockEndpoint mock = getMockEndpoint("mock:result"); + mock.expectedMessageCount(1); + + template.sendBody("direct:start", "<hello>world</hello>"); + + MockEndpoint.assertIsSatisfied(context); + + String xml = mock.getReceivedExchanges().get(0).getIn().getBody(String.class); + assertTrue(xml.contains("<transformed"), "Should contain transformed element"); + assertTrue(xml.contains("<hello>world</hello>"), "Should contain original content"); + } + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + @Override + public void configure() { + from("direct:start") + .to("xslt-saxon:dummy.xsl?uriResolver=#domResolver") + .to("mock:result"); + } + }; + } + + @Override + protected void bindToRegistry(Registry registry) throws Exception { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + DocumentBuilder db = dbf.newDocumentBuilder(); + Document xslDoc = db.parse(new InputSource(new StringReader(XSL))); + + URIResolver domResolver = new URIResolver() { + @Override + public Source resolve(String href, String base) throws TransformerException { + return new DOMSource(xslDoc); + } + }; + + registry.bind("domResolver", domResolver); + } +}
