XmlFactories.java
package net.sourceforge.plantuml.xml;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerFactory;
public class XmlFactories {
private XmlFactories() {
}
// This class uses the "initialization-on-demand holder" idiom to provide
// thread-safe
// lazy initialization of expensive factories.
// (see https://stackoverflow.com/a/8297830/1848731)
private static class DocumentBuilderFactoryHolder {
static final DocumentBuilderFactory INSTANCE = newHardenedDocumentBuilderFactory();
// Harden against XXE / billion-laughs / external schema fetch (CWE-611/CWE-776).
// PlantUML is often deployed as a server-side rendering service where the parsed
// XML (SVG, diagram fragments) may be attacker-controlled.
private static DocumentBuilderFactory newHardenedDocumentBuilderFactory() {
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
try {
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
factory.setXIncludeAware(false);
factory.setExpandEntityReferences(false);
} catch (Exception ignored) {
// best-effort: not all parsers support every feature
}
return factory;
}
}
private static class TransformerFactoryHolder {
static final TransformerFactory INSTANCE = newHardenedTransformerFactory();
private static TransformerFactory newHardenedTransformerFactory() {
final TransformerFactory factory = TransformerFactory.newInstance();
try {
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
} catch (Exception ignored) {
// best-effort: not all transformers support every attribute
}
return factory;
}
}
public static DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {
return DocumentBuilderFactoryHolder.INSTANCE.newDocumentBuilder();
}
public static Transformer newTransformer() throws TransformerConfigurationException {
return TransformerFactoryHolder.INSTANCE.newTransformer();
}
}