SvgUtils.java
package pro.verron.officestamper.utils.svg;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
import pro.verron.officestamper.utils.UtilsException;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
/// Utility class for working with SVG (Scalable Vector Graphics) documents.
/// Provides methods to parse SVG data securely, mitigating common security risks
/// such as XML External Entity (XXE) attacks.
public class SvgUtils {
private static volatile boolean restrictedMode = true;
private SvgUtils() {
/* This utility class should not be instantiated */
}
/// Returns whether SVG parsing safe mode is enabled.
///
/// When enabled (default), the parser is hardened against XXE/DTD and related attacks.
public static boolean isRestrictedMode() {
return restrictedMode;
}
/// Parse an SVG XML document from bytes with hardened XML parser settings.
///
/// - Disables DTDs and external entity resolution to prevent XXE attacks
/// - Enables secure processing
/// - Disables XInclude and entity expansion
///
/// @param bytes the SVG content as a UTF-8 encoded byte array
/// @return the parsed DOM Document
/// @throws UtilsException if parsing fails or the parser cannot be securely configured
public static Document parseDocument(byte[] bytes) {
var inputStream = new ByteArrayInputStream(bytes);
try {
var documentBuilder = restrictedMode ? newRestrictedDocumentBuilder() : newPermissiveDocumentBuilder();
return documentBuilder.parse(inputStream);
} catch (SAXException | IOException | ParserConfigurationException e) {
throw new UtilsException("Failed to parse SVG document securely", e);
}
}
private static DocumentBuilder newRestrictedDocumentBuilder()
throws ParserConfigurationException {
var factory = DocumentBuilderFactory.newInstance();
// Namespace-aware parsing is generally recommended for SVG
factory.setNamespaceAware(true);
// Harden against XXE/DTD
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
// Disallow any DOCTYPE
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
// Prevent external entity resolution
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);
// XInclude and entity expansion
try {
factory.setXIncludeAware(false);
} catch (UnsupportedOperationException ignored) {
// Some implementations may not support XInclude; safe to ignore
}
factory.setExpandEntityReferences(false);
return factory.newDocumentBuilder();
}
private static DocumentBuilder newPermissiveDocumentBuilder()
throws ParserConfigurationException {
var factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
// Intentionally avoid setting hardened features; caller opted out of safe mode.
// Keep entity expansion off to prevent excessive memory usage by default.
try {
factory.setXIncludeAware(false);
} catch (UnsupportedOperationException ignored) {
}
return factory.newDocumentBuilder();
}
/// Enables SVG parsing safe mode.
///
/// When safe mode is enabled, the SVG parser applies stricter security configurations to mitigate
/// potential vulnerabilities, such as XML External Entity (XXE) or Document Type Definition (DTD) attacks.
///
/// This method sets the internal flag to indicate that safe mode is active,
/// affecting the behavior of SVG parsing methods in this utility class.
public static void enableSafeMode() {
restrictedMode = true;
}
/// Disables the safe mode for SVG parsing.
///
/// When safe mode is disabled, the SVG parser applies relaxed security configurations,
/// which may reintroduce vulnerabilities, such as XML External Entity (XXE) or Document Type Definition (DTD)
/// attacks.
///
/// This method sets an internal flag to indicate that the safe mode is inactive,
/// affecting the behavior of SVG parsing methods in this utility class.
public static void disableSafeMode() {
restrictedMode = false;
}
}