diff --git a/core/fr.opensagres.xdocreport.core/src/main/java/fr/opensagres/xdocreport/core/utils/DOMUtils.java b/core/fr.opensagres.xdocreport.core/src/main/java/fr/opensagres/xdocreport/core/utils/DOMUtils.java
index ff02fe260..cf71cbd63 100644
--- a/core/fr.opensagres.xdocreport.core/src/main/java/fr/opensagres/xdocreport/core/utils/DOMUtils.java
+++ b/core/fr.opensagres.xdocreport.core/src/main/java/fr/opensagres/xdocreport/core/utils/DOMUtils.java
@@ -32,9 +32,11 @@
import java.util.Collection;
import java.util.Collections;
+import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
@@ -53,10 +55,52 @@
public class DOMUtils
{
+ private static final String DISALLOW_DOCTYPE_DECL = "http://apache.org/xml/features/disallow-doctype-decl";
+
+ private static final String LOAD_EXTERNAL_DTD = "http://apache.org/xml/features/nonvalidating/load-external-dtd";
+
+ private static final String EXTERNAL_GENERAL_ENTITIES = "http://xml.org/sax/features/external-general-entities";
+
+ private static final String EXTERNAL_PARAMETER_ENTITIES = "http://xml.org/sax/features/external-parameter-entities";
+
+ /**
+ * Returns a {@link DocumentBuilderFactory} hardened against XXE: DOCTYPE declarations are
+ * rejected and external entities/DTDs are not resolved (CVE-2025-65482).
+ */
+ public static DocumentBuilderFactory newSecureDocumentBuilderFactory()
+ throws ParserConfigurationException
+ {
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setFeature( DISALLOW_DOCTYPE_DECL, true );
+ factory.setFeature( EXTERNAL_GENERAL_ENTITIES, false );
+ factory.setFeature( EXTERNAL_PARAMETER_ENTITIES, false );
+ factory.setFeature( LOAD_EXTERNAL_DTD, false );
+ factory.setFeature( XMLConstants.FEATURE_SECURE_PROCESSING, true );
+ factory.setXIncludeAware( false );
+ factory.setExpandEntityReferences( false );
+ return factory;
+ }
+
+ /**
+ * Returns a {@link SAXParserFactory} hardened against XXE: DOCTYPE declarations are rejected
+ * and external entities/DTDs are not resolved (CVE-2025-65482).
+ */
+ public static SAXParserFactory newSecureSAXParserFactory()
+ throws ParserConfigurationException, SAXException
+ {
+ SAXParserFactory factory = SAXParserFactory.newInstance();
+ factory.setFeature( DISALLOW_DOCTYPE_DECL, true );
+ factory.setFeature( EXTERNAL_GENERAL_ENTITIES, false );
+ factory.setFeature( EXTERNAL_PARAMETER_ENTITIES, false );
+ factory.setFeature( LOAD_EXTERNAL_DTD, false );
+ factory.setFeature( XMLConstants.FEATURE_SECURE_PROCESSING, true );
+ return factory;
+ }
+
public static Document load( InputStream stream )
throws ParserConfigurationException, SAXException, IOException
{
- DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ DocumentBuilderFactory factory = newSecureDocumentBuilderFactory();
factory.setNamespaceAware( true );
DocumentBuilder builder = factory.newDocumentBuilder();
return builder.parse( stream );
@@ -65,7 +109,7 @@ public static Document load( InputStream stream )
public static Document load( String xml )
throws ParserConfigurationException, SAXException, IOException
{
- DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ DocumentBuilderFactory factory = newSecureDocumentBuilderFactory();
factory.setNamespaceAware( true );
DocumentBuilder builder = factory.newDocumentBuilder();
return builder.parse( IOUtils.toInputStream( xml, EncodingConstants.UTF_8.name() ) );
diff --git a/core/fr.opensagres.xdocreport.core/src/test/java/fr/opensagres/xdocreport/core/utils/DOMUtilsXxeTestCase.java b/core/fr.opensagres.xdocreport.core/src/test/java/fr/opensagres/xdocreport/core/utils/DOMUtilsXxeTestCase.java
new file mode 100644
index 000000000..3e5c1ee84
--- /dev/null
+++ b/core/fr.opensagres.xdocreport.core/src/test/java/fr/opensagres/xdocreport/core/utils/DOMUtilsXxeTestCase.java
@@ -0,0 +1,57 @@
+package fr.opensagres.xdocreport.core.utils;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.ByteArrayInputStream;
+import java.nio.charset.StandardCharsets;
+
+import javax.xml.parsers.SAXParser;
+
+import org.junit.Test;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * Regression test for the XXE hardening (incomplete fix of CVE-2025-65482): the secure
+ * factories returned by {@link DOMUtils} reject a DOCTYPE and do not resolve external entities.
+ */
+public class DOMUtilsXxeTestCase
+{
+
+ private static final String XXE =
+ "\n"
+ + " ]>\n"
+ + "&x;";
+
+ @Test
+ public void loadRejectsDoctype()
+ throws Exception
+ {
+ try
+ {
+ DOMUtils.load( new ByteArrayInputStream( XXE.getBytes( StandardCharsets.UTF_8 ) ) );
+ fail( "DOMUtils.load must reject a DOCTYPE" );
+ }
+ catch ( SAXException e )
+ {
+ assertTrue( e.getMessage() != null );
+ }
+ }
+
+ @Test
+ public void secureSAXParserRejectsDoctype()
+ throws Exception
+ {
+ SAXParser parser = DOMUtils.newSecureSAXParserFactory().newSAXParser();
+ try
+ {
+ parser.parse( new ByteArrayInputStream( XXE.getBytes( StandardCharsets.UTF_8 ) ), new DefaultHandler() );
+ fail( "Secure SAXParser must reject a DOCTYPE" );
+ }
+ catch ( SAXException e )
+ {
+ assertTrue( e.getMessage() != null );
+ }
+ }
+}
diff --git a/document/fr.opensagres.xdocreport.document.docx/src/main/java/fr/opensagres/xdocreport/document/docx/DocxReport.java b/document/fr.opensagres.xdocreport.document.docx/src/main/java/fr/opensagres/xdocreport/document/docx/DocxReport.java
index d91fc51e3..e0cef5d84 100644
--- a/document/fr.opensagres.xdocreport.document.docx/src/main/java/fr/opensagres/xdocreport/document/docx/DocxReport.java
+++ b/document/fr.opensagres.xdocreport.document.docx/src/main/java/fr/opensagres/xdocreport/document/docx/DocxReport.java
@@ -42,7 +42,6 @@
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
-import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.SAXException;
@@ -53,6 +52,7 @@
import fr.opensagres.xdocreport.core.io.IEntryReaderProvider;
import fr.opensagres.xdocreport.core.io.IEntryWriterProvider;
import fr.opensagres.xdocreport.core.io.XDocArchive;
+import fr.opensagres.xdocreport.core.utils.DOMUtils;
import fr.opensagres.xdocreport.document.AbstractXDocReport;
import fr.opensagres.xdocreport.document.docx.images.DocxImageRegistry;
import fr.opensagres.xdocreport.document.docx.preprocessor.DefaultStyle;
@@ -162,7 +162,7 @@ protected void onBeforePreprocessing( Map sharedContext, XDocArc
try
{
HyperlinkContentHandler contentHandler = new HyperlinkContentHandler();
- SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
+ SAXParser saxParser = DOMUtils.newSecureSAXParserFactory().newSAXParser();
saxParser.parse(preprocessedArchive.getEntryInputStream(relsEntryName), contentHandler);
if ( contentHandler.getHyperlinks() != null )
{
diff --git a/document/fr.opensagres.xdocreport.document/src/main/java/fr/opensagres/xdocreport/document/preprocessor/sax/SAXXDocPreprocessor.java b/document/fr.opensagres.xdocreport.document/src/main/java/fr/opensagres/xdocreport/document/preprocessor/sax/SAXXDocPreprocessor.java
index 34ed0aabe..1a24f03ed 100644
--- a/document/fr.opensagres.xdocreport.document/src/main/java/fr/opensagres/xdocreport/document/preprocessor/sax/SAXXDocPreprocessor.java
+++ b/document/fr.opensagres.xdocreport.document/src/main/java/fr/opensagres/xdocreport/document/preprocessor/sax/SAXXDocPreprocessor.java
@@ -57,6 +57,7 @@ public boolean preprocess( String entryName, InputStream reader, Writer writer,
{
XMLReader xmlReader = XMLReaderFactory.createXMLReader();
//To avoid xxe security issue
+ xmlReader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
xmlReader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
xmlReader.setFeature("http://xml.org/sax/features/external-general-entities", false);
xmlReader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);