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);
}