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 20a3c9d83f8e CAMEL-26498: camel-xml-jaxp - stream caching a SAXSource 
without an XMLReader parses it with the secure XMLReader (#28979)
20a3c9d83f8e is described below

commit 20a3c9d83f8ee467bb500e9472ce78eef422737e
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Nov 2 21:49:07 2026 +0100

    CAMEL-26498: camel-xml-jaxp - stream caching a SAXSource without an 
XMLReader parses it with the secure XMLReader (#28979)
    
    …MLReader parses it with the secure XMLReader
    
    StreamSourceConverter.convertToStreamCache(SAXSource) turned a SAXSource
    into a String with a Transformer. When the SAXSource had no XMLReader, the
    TransformerFactory parsed it with its own default parser and settings, which
    depend on the JAXP implementation on the classpath.
    
    Now a SAXSource without an XMLReader gets the secure XMLReader of
    XmlConverter (the same one the SAXSource instances created by XmlConverter
    carry) before the transform. XmlConverter.createXMLReader() is extracted 
from
    toSAXSourceFromStream so both use the same pooled reader.
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../converter/stream/StreamCacheConverterTest.java | 27 +++++++++++++++++++++-
 .../apache/camel/converter/jaxp/XmlConverter.java  | 16 +++++++++----
 .../camel/util/xml/StreamSourceConverter.java      | 14 ++++++++++-
 3 files changed, 51 insertions(+), 6 deletions(-)

diff --git 
a/core/camel-core/src/test/java/org/apache/camel/converter/stream/StreamCacheConverterTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/converter/stream/StreamCacheConverterTest.java
index a27a9ca3351f..92e90c0ebc07 100644
--- 
a/core/camel-core/src/test/java/org/apache/camel/converter/stream/StreamCacheConverterTest.java
+++ 
b/core/camel-core/src/test/java/org/apache/camel/converter/stream/StreamCacheConverterTest.java
@@ -19,6 +19,9 @@ package org.apache.camel.converter.stream;
 import java.io.ByteArrayInputStream;
 import java.io.InputStream;
 import java.io.Serializable;
+import java.io.StringReader;
+import java.nio.file.Files;
+import java.nio.file.Path;
 
 import javax.xml.transform.Source;
 import javax.xml.transform.sax.SAXSource;
@@ -35,6 +38,7 @@ import org.apache.camel.support.DefaultExchange;
 import org.apache.camel.util.xml.StreamSourceConverter;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -57,7 +61,7 @@ public class StreamCacheConverterTest extends 
ContextTestSupport {
     }
 
     @Test
-    public void testConvertToStreamCache() {
+    public void testConvertToStreamCache() throws Exception {
         context.start();
 
         ByteArrayInputStream inputStream = new 
ByteArrayInputStream(MESSAGE.getBytes());
@@ -68,6 +72,27 @@ public class StreamCacheConverterTest extends 
ContextTestSupport {
         assertEquals(MESSAGE, message, "The converted message is wrong");
     }
 
+    @Test
+    public void 
testConvertToStreamCacheSAXSourceWithoutXMLReaderDoesNotExpandExternalEntity(@TempDir
 Path dir)
+            throws Exception {
+        context.start();
+
+        Path secret = dir.resolve("secret.txt");
+        Files.writeString(secret, "TOP-SECRET-CONTENT");
+        String xml = "<?xml version=\"1.0\"?>\n"
+                     + "<!DOCTYPE test [<!ENTITY ext SYSTEM \"" + 
secret.toUri() + "\">]>\n"
+                     + "<test>&ext;</test>";
+
+        // a SAXSource without an XMLReader is parsed with the secure 
XMLReader of XmlConverter,
+        // like the SAXSource instances that XmlConverter creates
+        SAXSource source = new SAXSource(new InputSource(new 
StringReader(xml)));
+        // stream caching converts the body with the type converter
+        StreamCache streamCache = 
context.getTypeConverter().mandatoryConvertTo(StreamCache.class, exchange, 
source);
+        String message = context.getTypeConverter().convertTo(String.class, 
streamCache);
+        // the secure XMLReader does not load external entities, so the entity 
is skipped
+        assertEquals("<test/>", message, "The external entity must not be 
expanded");
+    }
+
     @Test
     public void testConvertToStreamCacheStreamSource() throws Exception {
         context.start();
diff --git 
a/core/camel-xml-jaxp/src/main/java/org/apache/camel/converter/jaxp/XmlConverter.java
 
b/core/camel-xml-jaxp/src/main/java/org/apache/camel/converter/jaxp/XmlConverter.java
index fa1856973a60..be8fe607a57d 100644
--- 
a/core/camel-xml-jaxp/src/main/java/org/apache/camel/converter/jaxp/XmlConverter.java
+++ 
b/core/camel-xml-jaxp/src/main/java/org/apache/camel/converter/jaxp/XmlConverter.java
@@ -481,10 +481,7 @@ public class XmlConverter {
 
         XMLReader xmlReader = null;
         try {
-            if (xmlReaderPool == null) {
-                xmlReaderPool = new XMLReaderPool(createSAXParserFactory());
-            }
-            xmlReader = xmlReaderPool.createXMLReader();
+            xmlReader = createXMLReader();
         } catch (Exception ex) {
             LOG.warn("Cannot create the SAXParser XMLReader, due to {}", 
ex.getMessage(), ex);
         }
@@ -1197,6 +1194,17 @@ public class XmlConverter {
         return null;
     }
 
+    /**
+     * Creates an {@link XMLReader} with the secure settings of {@link 
#createSAXParserFactory()}, taken from a pool of
+     * readers that are each used for one parse.
+     */
+    public XMLReader createXMLReader() throws SAXException, 
ParserConfigurationException {
+        if (xmlReaderPool == null) {
+            xmlReaderPool = new XMLReaderPool(createSAXParserFactory());
+        }
+        return xmlReaderPool.createXMLReader();
+    }
+
     public SAXParserFactory createSAXParserFactory() {
         SAXParserFactory sfactory = SAXParserFactory.newInstance();
         // Need to setup XMLReader security feature by default
diff --git 
a/core/camel-xml-jaxp/src/main/java/org/apache/camel/util/xml/StreamSourceConverter.java
 
b/core/camel-xml-jaxp/src/main/java/org/apache/camel/util/xml/StreamSourceConverter.java
index 994711efe677..c10aba64cfbf 100644
--- 
a/core/camel-xml-jaxp/src/main/java/org/apache/camel/util/xml/StreamSourceConverter.java
+++ 
b/core/camel-xml-jaxp/src/main/java/org/apache/camel/util/xml/StreamSourceConverter.java
@@ -19,12 +19,16 @@ package org.apache.camel.util.xml;
 import java.io.IOException;
 import java.io.Serializable;
 
+import javax.xml.parsers.ParserConfigurationException;
 import javax.xml.transform.sax.SAXSource;
 import javax.xml.transform.stream.StreamSource;
 
+import org.xml.sax.SAXException;
+
 import org.apache.camel.Converter;
 import org.apache.camel.Exchange;
 import org.apache.camel.StreamCache;
+import org.apache.camel.converter.jaxp.XmlConverter;
 
 import static 
org.apache.camel.converter.stream.StreamCacheConverter.convertToByteArray;
 
@@ -35,6 +39,8 @@ import static 
org.apache.camel.converter.stream.StreamCacheConverter.convertToBy
 @Converter(generateBulkLoader = true)
 public final class StreamSourceConverter {
 
+    private static final XmlConverter XML_CONVERTER = new XmlConverter();
+
     /**
      * Utility classes should not have a public constructor.
      */
@@ -53,7 +59,13 @@ public final class StreamSourceConverter {
     }
 
     @Converter(order = 3)
-    public static StreamCache convertToStreamCache(SAXSource source, Exchange 
exchange) {
+    public static StreamCache convertToStreamCache(SAXSource source, Exchange 
exchange)
+            throws SAXException, ParserConfigurationException {
+        if (source.getXMLReader() == null) {
+            // parse with the same secure XMLReader as the SAXSource instances 
that XmlConverter creates,
+            // instead of the default parser of the TransformerFactory
+            source = new SAXSource(XML_CONVERTER.createXMLReader(), 
source.getInputSource());
+        }
         String data = 
exchange.getContext().getTypeConverter().convertTo(String.class, exchange, 
source);
         return new SourceCache(data);
     }

Reply via email to