Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 );
Expand All @@ -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() ) );
Expand Down
Original file line number Diff line number Diff line change
@@ -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 =
"<?xml version=\"1.0\"?>\n"
+ "<!DOCTYPE r [ <!ENTITY x SYSTEM \"file:///etc/hostname\"> ]>\n"
+ "<r>&x;</r>";

@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 );
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.SAXException;

Expand All @@ -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;
Expand Down Expand Up @@ -162,7 +162,7 @@ protected void onBeforePreprocessing( Map<String, Object> 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 )
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down