Merge pull request #1082 from langfr/feature/xmlTools

Centralize and secure DocumentBuilder creation.
This commit is contained in:
Jochen Staerk
2026-04-07 15:46:11 +02:00
committed by GitHub
9 changed files with 91 additions and 111 deletions

View File

@@ -4,6 +4,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Date;
@@ -11,11 +12,19 @@ import org.apache.commons.io.IOUtils;
import org.dom4j.io.XMLWriter;
import org.mustangproject.ZUGFeRD.ZUGFeRDDateFormat;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerFactory;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
public class XMLTools extends XMLWriter {
@Override
@@ -28,29 +37,85 @@ public class XMLTools extends XMLWriter {
return super.escapeElementEntities(s);
}
public static DocumentBuilder getDocumentBuilder() throws ParserConfigurationException {
public static DocumentBuilder getDocumentBuilder(boolean namespaceAware) throws ParserConfigurationException {
final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
//REDHAT
//https://www.blackhat.com/docs/us-15/materials/us-15-Wang-FileCry-The-New-Age-Of-XXE-java-wp.pdf
try {
dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
} catch (ParserConfigurationException e) {
// ignore
}
try {
dbf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
} catch (IllegalArgumentException e) {
// ignore
}
try {
dbf.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
} catch (IllegalArgumentException e) {
// ignore
}
//OWASP
//https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html
try {
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
} catch (ParserConfigurationException e) {
// ignore
}
try {
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
} catch (ParserConfigurationException e) {
// ignore
}
try {
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
} catch (ParserConfigurationException e) {
// ignore
}
try {
// Disable external DTDs as well
dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
} catch (ParserConfigurationException e) {
// ignore
}
// and these as well, per Timothy Morgan's 2014 paper: "XML Schema, DTD, and Entity Attacks"
dbf.setXIncludeAware(false);
dbf.setExpandEntityReferences(false);
dbf.setNamespaceAware(true);
dbf.setNamespaceAware(namespaceAware);
return dbf.newDocumentBuilder();
}
public static Validator getValidator(URL schemaFile) throws SAXException
{
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
try {
schemaFactory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
} catch (SAXNotSupportedException | SAXNotRecognizedException e) {
// ignore
}
Schema schema = schemaFactory.newSchema(schemaFile);
Validator validator = schema.newValidator();
try {
validator.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
} catch (SAXNotSupportedException | SAXNotRecognizedException e) {
// ignore
}
return validator;
}
public static TransformerFactory getTransformerFactory()
{
TransformerFactory factory = new net.sf.saxon.TransformerFactoryImpl();
try {
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
} catch (TransformerConfigurationException e) {
// ignore
}
return factory;
}
public static String nDigitFormat(BigDecimal value, int scale) {
/*

View File

@@ -15,6 +15,7 @@ import org.apache.fop.configuration.ConfigurationException;
import org.apache.fop.configuration.DefaultConfigurationBuilder;
import org.apache.xmlgraphics.util.MimeConstants;
import org.mustangproject.ClasspathResolverURIAdapter;
import org.mustangproject.XMLTools;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -42,8 +43,7 @@ public class ValidationLogVisualizer {
public ValidationLogVisualizer() {
mFactory = new net.sf.saxon.TransformerFactoryImpl();
// fact = TransformerFactory.newInstance();
mFactory = XMLTools.getTransformerFactory();
mFactory.setURIResolver(new ValidationLogVisualizer.ClasspathResourceURIResolver());
}

View File

@@ -16,6 +16,8 @@ import javax.xml.transform.URIResolver;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.mustangproject.XMLTools;
/***
* Uses a XSLT transformation to upgrade
@@ -31,8 +33,7 @@ public class XMLUpgrader {
private Templates mXsltTemplate = null;
public XMLUpgrader() {
mFactory = new net.sf.saxon.TransformerFactoryImpl();
//fact = TransformerFactory.newInstance();
mFactory = XMLTools.getTransformerFactory();
mFactory.setURIResolver(new ClasspathResourceURIResolver());
}

View File

@@ -18,9 +18,7 @@ import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
@@ -341,7 +339,7 @@ public class ZUGFeRDInvoiceImporter {
final ByteArrayInputStream is = new ByteArrayInputStream(rawXML);
/// is.skip(guessBOMSize(is));
try {
DocumentBuilder builder = XMLTools.getDocumentBuilder();
DocumentBuilder builder = XMLTools.getDocumentBuilder(true);
document = builder.parse(is);
} catch (Exception e) {
@@ -356,7 +354,7 @@ public class ZUGFeRDInvoiceImporter {
final ByteArrayInputStream is = new ByteArrayInputStream(rawXML);
/// is.skip(guessBOMSize(is));
DocumentBuilder builder = XMLTools.getDocumentBuilder();
DocumentBuilder builder = XMLTools.getDocumentBuilder(true);
if (canParse()) {
document = builder.parse(is);
if (parseAutomatically) {

View File

@@ -32,6 +32,7 @@ import org.apache.fop.configuration.DefaultConfigurationBuilder;
import org.apache.xmlgraphics.util.MimeConstants;
import org.mustangproject.ClasspathResolverURIAdapter;
import org.mustangproject.EStandard;
import org.mustangproject.XMLTools;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
@@ -39,7 +40,6 @@ import org.w3c.dom.Element;
import org.xml.sax.InputSource;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.*;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamResult;
@@ -47,8 +47,6 @@ import javax.xml.transform.stream.StreamSource;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
@@ -85,8 +83,7 @@ public class ZUGFeRDVisualizer {
private Templates mXsltZF1HTMLTemplate = null;
public ZUGFeRDVisualizer() {
mFactory = new net.sf.saxon.TransformerFactoryImpl();
// fact = TransformerFactory.newInstance();
mFactory = XMLTools.getTransformerFactory();
mFactory.setURIResolver(new ClasspathResourceURIResolver());
}
@@ -104,40 +101,8 @@ public class ZUGFeRDVisualizer {
String ublCreditNoteSignature = "CreditNote";
String cioSignature = "SCRDMCCBDACIOMessageStructure";
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
//REDHAT
//https://www.blackhat.com/docs/us-15/materials/us-15-Wang-FileCry-The-New-Age-Of-XXE-java-wp.pdf
dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
try
{
dbf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
}
catch (IllegalArgumentException e)
{
LOGGER.warn("Property: \"Access external DTD\" not supported.");
}
try
{
dbf.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
}
catch (IllegalArgumentException e)
{
LOGGER.warn("Property: \"Access external schema\" not supported.");
}
//OWASP
//https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
// Disable external DTDs as well
dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
// and these as well, per Timothy Morgan's 2014 paper: "XML Schema, DTD, and Entity Attacks"
dbf.setXIncludeAware(false);
dbf.setExpandEntityReferences(false);
dbf.setNamespaceAware(true);
try {
DocumentBuilder db = dbf.newDocumentBuilder();
DocumentBuilder db = XMLTools.getDocumentBuilder(true);
Document doc = db.parse(new InputSource(fis));
Element root = doc.getDocumentElement();
if (root.getLocalName().equals(zf1Signature)) {

View File

@@ -14,7 +14,6 @@ import java.util.HashMap;
import java.util.Set;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
@@ -23,6 +22,7 @@ import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.mustangproject.util.ByteArraySearcher;
import org.mustangproject.XMLTools;
import org.mustangproject.ZUGFeRD.ZUGFeRDImporter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -142,12 +142,7 @@ public class PDFValidator extends Validator {
* <zf:Version>1.0</zf:Version>
*/
try {
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
// and these as well, per Timothy Morgan's 2014 paper: "XML Schema, DTD, and Entity Attacks"
factory.setXIncludeAware(false);
final DocumentBuilder builder = factory.newDocumentBuilder();
final DocumentBuilder builder = XMLTools.getDocumentBuilder(false);
final InputSource is = new InputSource(new StringReader(xmp));
docXMP = builder.parse(is);

View File

@@ -7,9 +7,8 @@ import java.net.URL;
import javax.xml.XMLConstants;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import org.mustangproject.XMLTools;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;
@@ -59,11 +58,8 @@ public abstract class Validator {
protected void validateSchema(byte[] xmlRawData, String schemaPath, int section, EPart part) throws IrrecoverableValidationError {
URL schemaFile = Thread.currentThread().getContextClassLoader().getResource("schema/" + schemaPath);
Source xmlData = new StreamSource(new ByteArrayInputStream(xmlRawData));
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
try {
schemaFactory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
Schema schema = schemaFactory.newSchema(schemaFile);
javax.xml.validation.Validator validator = schema.newValidator();
javax.xml.validation.Validator validator = XMLTools.getValidator(schemaFile);
validator.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
validator.validate(xmlData);
} catch (SAXException e) {

View File

@@ -13,9 +13,7 @@ import java.text.ParseException;
import java.util.Calendar;
import java.util.Set;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.stream.StreamSource;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
@@ -156,26 +154,7 @@ public class XMLValidator extends Validator {
*
*/
final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
//REDHAT
//https://www.blackhat.com/docs/us-15/materials/us-15-Wang-FileCry-The-New-Age-Of-XXE-java-wp.pdf
dbf.setAttribute(XMLConstants.FEATURE_SECURE_PROCESSING, true);
dbf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
dbf.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
//OWASP
//https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
// Disable external DTDs as well
dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
// and these as well, per Timothy Morgan's 2014 paper: "XML Schema, DTD, and Entity Attacks"
dbf.setXIncludeAware(false);
dbf.setExpandEntityReferences(false);
dbf.setNamespaceAware(true);
final DocumentBuilder db = dbf.newDocumentBuilder();
final DocumentBuilder db = XMLTools.getDocumentBuilder(true);
final InputSource is = new InputSource(new StringReader(zfXML));
final Document doc = db.parse(is);

View File

@@ -17,9 +17,7 @@ import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.commons.io.IOUtils;
import org.dom4j.DocumentException;
@@ -143,24 +141,7 @@ public class ZUGFeRDValidator {
boolean isXML = false;
String xmlAsString = null;
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
//REDHAT
//https://www.blackhat.com/docs/us-15/materials/us-15-Wang-FileCry-The-New-Age-Of-XXE-java-wp.pdf
dbf.setAttribute(XMLConstants.FEATURE_SECURE_PROCESSING, true);
dbf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
dbf.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
//OWASP
//https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
// Disable external DTDs as well
dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
// and these as well, per Timothy Morgan's 2014 paper: "XML Schema, DTD, and Entity Attacks"
dbf.setXIncludeAware(false);
dbf.setExpandEntityReferences(false);
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();
DocumentBuilder db = XMLTools.getDocumentBuilder(true);
content = XMLTools.removeBOM(content);
xmlAsString = new String(content, StandardCharsets.UTF_8);