86fbe1
From 4b80871cb72389d67aa1ab1fb91091c22f1a6100 Mon Sep 17 00:00:00 2001
86fbe1
From: Ondrej Dubaj <odubaj@redhat.com>
86fbe1
Date: Fri, 24 Jul 2020 14:08:54 +0200
86fbe1
Subject: [PATCH] Fix for XXE vulnerability
86fbe1
86fbe1
by defaulting to disabling external access and doc types. The
86fbe1
legacy insecure behavior can be restored via the new connection property xmlFactoryFactory
86fbe1
with a value of LEGACY_INSECURE. Alternatively, a custom class name can be specified that
86fbe1
implements org.postgresql.xml.PGXmlFactoryFactory and takes a no argument constructor.
86fbe1
86fbe1
* refactor: Clean up whitespace in existing PgSQLXMLTest
86fbe1
* fix: Fix XXE vulnerability in PgSQLXML by disabling external access and doctypes
86fbe1
* fix: Add missing getter and setter for XML_FACTORY_FACTORY to BasicDataSource
86fbe1
---
86fbe1
 build.xml                                     |   4 +
86fbe1
 org/postgresql/Driver.java.in                 |   4 +-
86fbe1
 org/postgresql/core/BaseConnection.java       |   9 ++
86fbe1
 org/postgresql/ds/common/BaseDataSource.java  |  10 ++
86fbe1
 org/postgresql/jdbc4/Jdbc4Connection.java     |  44 ++++++
86fbe1
 org/postgresql/jdbc4/Jdbc4SQLXML.java         |  37 +++--
86fbe1
 .../xml/DefaultPGXmlFactoryFactory.java       | 140 ++++++++++++++++++
86fbe1
 .../xml/EmptyStringEntityResolver.java        |  23 +++
86fbe1
 .../LegacyInsecurePGXmlFactoryFactory.java    |  57 +++++++
86fbe1
 org/postgresql/xml/NullErrorHandler.java      |  25 ++++
86fbe1
 org/postgresql/xml/PGXmlFactoryFactory.java   |  30 ++++
86fbe1
 11 files changed, 362 insertions(+), 21 deletions(-)
86fbe1
 create mode 100644 org/postgresql/xml/DefaultPGXmlFactoryFactory.java
86fbe1
 create mode 100644 org/postgresql/xml/EmptyStringEntityResolver.java
86fbe1
 create mode 100644 org/postgresql/xml/LegacyInsecurePGXmlFactoryFactory.java
86fbe1
 create mode 100644 org/postgresql/xml/NullErrorHandler.java
86fbe1
 create mode 100644 org/postgresql/xml/PGXmlFactoryFactory.java
86fbe1
86fbe1
diff --git a/build.xml b/build.xml
86fbe1
index c9c5660..59b81bf 100644
86fbe1
--- a/build.xml
86fbe1
+++ b/build.xml
86fbe1
@@ -133,6 +133,7 @@
86fbe1
        <include name="${package}/geometric/**" />
86fbe1
        <include name="${package}/largeobject/**" />
86fbe1
        <include name="${package}/util/**" />
86fbe1
+       <include name="${package}/xml/**" />
86fbe1
 
86fbe1
        
86fbe1
        Each jdbcN subpackage is used only if the driver supports *at least* that
86fbe1
@@ -193,6 +194,9 @@
86fbe1
        <include name="${package}/xa/jdbc3/*.java" />
86fbe1
        <include name="${package}/xa/jdbc4/*.java" if="jdbc4any" />
86fbe1
 
86fbe1
+       
86fbe1
+       <include name="${package}/xml/*.java" if="jdbc4any" />
86fbe1
+
86fbe1
     </javac>
86fbe1
   </target>
86fbe1
 
86fbe1
diff --git a/org/postgresql/Driver.java.in b/org/postgresql/Driver.java.in
86fbe1
index f6c69c0..669f0b6 100644
86fbe1
--- a/org/postgresql/Driver.java.in
86fbe1
+++ b/org/postgresql/Driver.java.in
86fbe1
@@ -465,7 +465,9 @@ public class Driver implements java.sql.Driver
86fbe1
                 { "kerberosServerName", Boolean.FALSE,
86fbe1
                   "The Kerberos service name to use when authenticating with GSSAPI.  This is equivalent to libpq's PGKRBSRVNAME environment variable." },
86fbe1
                 { "jaasApplicationName", Boolean.FALSE,
86fbe1
-                  "Specifies the name of the JAAS system or application login configuration." }
86fbe1
+                  "Specifies the name of the JAAS system or application login configuration." },
86fbe1
+                { "xmlFactoryFactory", Boolean.FALSE,
86fbe1
+                  "Factory class to instantiate factories for XML processing" }
86fbe1
             };
86fbe1
 
86fbe1
     /**
86fbe1
diff --git a/org/postgresql/core/BaseConnection.java b/org/postgresql/core/BaseConnection.java
86fbe1
index 6373d53..69192af 100644
86fbe1
--- a/org/postgresql/core/BaseConnection.java
86fbe1
+++ b/org/postgresql/core/BaseConnection.java
86fbe1
@@ -10,6 +10,7 @@ package org.postgresql.core;
86fbe1
 import java.sql.*;
86fbe1
 import org.postgresql.PGConnection;
86fbe1
 import org.postgresql.jdbc2.TimestampUtils;
86fbe1
+import org.postgresql.xml.PGXmlFactoryFactory;
86fbe1
 
86fbe1
 /**
86fbe1
  * Driver-internal connection interface. Application code should not use
86fbe1
@@ -154,4 +155,12 @@ public interface BaseConnection extends PGConnection, Connection
86fbe1
      * @return True for binary transfer, false for text transfer.
86fbe1
      */
86fbe1
     public boolean binaryTransferSend(int oid);
86fbe1
+
86fbe1
+    /**
86fbe1
+   * Retrieve the factory to instantiate XML processing factories.
86fbe1
+   *
86fbe1
+   * @return The factory to use to instantiate XML processing factories
86fbe1
+   * @throws SQLException if the class cannot be found or instantiated.
86fbe1
+   */
86fbe1
+  PGXmlFactoryFactory getXmlFactoryFactory() throws SQLException;
86fbe1
 }
86fbe1
diff --git a/org/postgresql/ds/common/BaseDataSource.java b/org/postgresql/ds/common/BaseDataSource.java
86fbe1
index bfe54f7..9be3c4c 100644
86fbe1
--- a/org/postgresql/ds/common/BaseDataSource.java
86fbe1
+++ b/org/postgresql/ds/common/BaseDataSource.java
86fbe1
@@ -16,6 +16,7 @@ import java.io.ObjectInputStream;
86fbe1
 import java.io.ObjectOutputStream;
86fbe1
 import java.io.ByteArrayInputStream;
86fbe1
 import java.io.ByteArrayOutputStream;
86fbe1
+import java.util.Properties;
86fbe1
 
86fbe1
 /**
86fbe1
  * Base class for data sources and related classes.
86fbe1
@@ -63,6 +64,7 @@ public abstract class BaseDataSource implements Referenceable
86fbe1
     private int logLevel = 0;
86fbe1
     private int protocolVersion = 0;
86fbe1
     private String applicationName;
86fbe1
+    private String xmlFactoryFactory = "";
86fbe1
 
86fbe1
     /**
86fbe1
      * Gets a connection to the PostgreSQL database.  The database is identified by the
86fbe1
@@ -629,4 +631,12 @@ public abstract class BaseDataSource implements Referenceable
86fbe1
         readBaseObject(ois);
86fbe1
     }
86fbe1
 
86fbe1
+    public String getXmlFactoryFactory() {
86fbe1
+        return this.xmlFactoryFactory;
86fbe1
+    }
86fbe1
+
86fbe1
+    public void setXmlFactoryFactory(String xmlFactoryFactory) {
86fbe1
+        this.xmlFactoryFactory = xmlFactoryFactory;
86fbe1
+    }
86fbe1
+
86fbe1
 }
86fbe1
diff --git a/org/postgresql/jdbc4/Jdbc4Connection.java b/org/postgresql/jdbc4/Jdbc4Connection.java
86fbe1
index 209b970..956a545 100644
86fbe1
--- a/org/postgresql/jdbc4/Jdbc4Connection.java
86fbe1
+++ b/org/postgresql/jdbc4/Jdbc4Connection.java
86fbe1
@@ -10,6 +10,12 @@ package org.postgresql.jdbc4;
86fbe1
 import java.util.Map;
86fbe1
 import java.util.Properties;
86fbe1
 import java.sql.SQLException;
86fbe1
+import org.postgresql.util.GT;
86fbe1
+import org.postgresql.util.PSQLState;
86fbe1
+import org.postgresql.xml.DefaultPGXmlFactoryFactory;
86fbe1
+import org.postgresql.xml.LegacyInsecurePGXmlFactoryFactory;
86fbe1
+import org.postgresql.xml.PGXmlFactoryFactory;
86fbe1
+import org.postgresql.util.PSQLException;
86fbe1
 
86fbe1
 import org.postgresql.util.HostSpec;
86fbe1
 
86fbe1
@@ -20,8 +26,13 @@ import org.postgresql.util.HostSpec;
86fbe1
  */
86fbe1
 public class Jdbc4Connection extends AbstractJdbc4Connection implements java.sql.Connection
86fbe1
 {
86fbe1
+
86fbe1
+    private final String xmlFactoryFactoryClass;
86fbe1
+    private PGXmlFactoryFactory xmlFactoryFactory;
86fbe1
+
86fbe1
     public Jdbc4Connection(HostSpec[] hostSpecs, String user, String database, Properties info, String url) throws SQLException {
86fbe1
         super(hostSpecs, user, database, info, url);
86fbe1
+        xmlFactoryFactoryClass = info.getProperty("xmlFactoryFactory");
86fbe1
     }
86fbe1
 
86fbe1
     public java.sql.Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException
86fbe1
@@ -62,4 +73,37 @@ public class Jdbc4Connection extends AbstractJdbc4Connection implements java.sql
86fbe1
         setTypeMapImpl(map);
86fbe1
     }
86fbe1
 
86fbe1
+      @Override
86fbe1
+    public PGXmlFactoryFactory getXmlFactoryFactory() throws SQLException {
86fbe1
+        if (xmlFactoryFactory == null) {
86fbe1
+            if (xmlFactoryFactoryClass == null || xmlFactoryFactoryClass.equals("")) {
86fbe1
+                xmlFactoryFactory = DefaultPGXmlFactoryFactory.INSTANCE;
86fbe1
+            } else if (xmlFactoryFactoryClass.equals("LEGACY_INSECURE")) {
86fbe1
+                xmlFactoryFactory = LegacyInsecurePGXmlFactoryFactory.INSTANCE;
86fbe1
+            } else {
86fbe1
+                Class clazz;
86fbe1
+                try {
86fbe1
+                    clazz = Class.forName(xmlFactoryFactoryClass);
86fbe1
+                } catch (ClassNotFoundException ex) {
86fbe1
+                    throw new PSQLException(
86fbe1
+                        GT.tr("Could not instantiate xmlFactoryFactory: {0}", xmlFactoryFactoryClass),
86fbe1
+                        PSQLState.INVALID_PARAMETER_VALUE, ex);
86fbe1
+                }
86fbe1
+                if (!clazz.isAssignableFrom(PGXmlFactoryFactory.class)) {
86fbe1
+                    throw new PSQLException(
86fbe1
+                        GT.tr("Connection property xmlFactoryFactory must implement PGXmlFactoryFactory: {0}", xmlFactoryFactoryClass),
86fbe1
+                        PSQLState.INVALID_PARAMETER_VALUE);
86fbe1
+                }
86fbe1
+                try {
86fbe1
+                    xmlFactoryFactory = (PGXmlFactoryFactory) clazz.newInstance();
86fbe1
+                } catch (Exception ex) {
86fbe1
+                    throw new PSQLException(
86fbe1
+                        GT.tr("Could not instantiate xmlFactoryFactory: {0}", xmlFactoryFactoryClass),
86fbe1
+                        PSQLState.INVALID_PARAMETER_VALUE, ex);
86fbe1
+                }       
86fbe1
+            }
86fbe1
+        }
86fbe1
+        return xmlFactoryFactory;
86fbe1
+    }
86fbe1
+
86fbe1
 }
86fbe1
diff --git a/org/postgresql/jdbc4/Jdbc4SQLXML.java b/org/postgresql/jdbc4/Jdbc4SQLXML.java
86fbe1
index 2302185..7b4ebba 100644
86fbe1
--- a/org/postgresql/jdbc4/Jdbc4SQLXML.java
86fbe1
+++ b/org/postgresql/jdbc4/Jdbc4SQLXML.java
86fbe1
@@ -4,6 +4,8 @@ import org.postgresql.util.GT;
86fbe1
 import org.postgresql.util.PSQLState;
86fbe1
 import org.postgresql.util.PSQLException;
86fbe1
 import org.postgresql.core.BaseConnection;
86fbe1
+import org.postgresql.xml.PGXmlFactoryFactory;
86fbe1
+import org.postgresql.xml.DefaultPGXmlFactoryFactory;
86fbe1
 
86fbe1
 import java.io.*;
86fbe1
 import java.sql.SQLXML;
86fbe1
@@ -14,7 +16,6 @@ import javax.xml.transform.Result;
86fbe1
 import javax.xml.transform.dom.DOMSource;
86fbe1
 import javax.xml.transform.dom.DOMResult;
86fbe1
 import javax.xml.parsers.DocumentBuilder;
86fbe1
-import javax.xml.parsers.DocumentBuilderFactory;
86fbe1
 import javax.xml.transform.Transformer;
86fbe1
 import javax.xml.transform.TransformerFactory;
86fbe1
 import javax.xml.transform.TransformerException;
86fbe1
@@ -22,8 +23,7 @@ import javax.xml.transform.TransformerException;
86fbe1
 import javax.xml.transform.sax.SAXSource;
86fbe1
 import javax.xml.transform.sax.SAXResult;
86fbe1
 import org.xml.sax.InputSource;
86fbe1
-import org.xml.sax.ErrorHandler;
86fbe1
-import org.xml.sax.SAXParseException;
86fbe1
+import org.xml.sax.XMLReader;
86fbe1
 import javax.xml.transform.sax.SAXTransformerFactory;
86fbe1
 import javax.xml.transform.sax.TransformerHandler;
86fbe1
 
86fbe1
@@ -69,6 +69,13 @@ public class Jdbc4SQLXML implements SQLXML {
86fbe1
         _freed = false;
86fbe1
     }
86fbe1
 
86fbe1
+    private PGXmlFactoryFactory getXmlFactoryFactory() throws SQLException {
86fbe1
+        if (_conn != null) {
86fbe1
+            return _conn.getXmlFactoryFactory();
86fbe1
+        }
86fbe1
+        return DefaultPGXmlFactoryFactory.INSTANCE;
86fbe1
+    }
86fbe1
+
86fbe1
     public synchronized void free()
86fbe1
     {
86fbe1
         _freed = true;
86fbe1
@@ -122,16 +129,15 @@ public class Jdbc4SQLXML implements SQLXML {
86fbe1
         try {
86fbe1
             if (sourceClass == null || DOMSource.class.equals(sourceClass))
86fbe1
             {
86fbe1
-                DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
86fbe1
-                DocumentBuilder builder = factory.newDocumentBuilder();
86fbe1
-                builder.setErrorHandler(new NonPrintingErrorHandler());
86fbe1
+                DocumentBuilder builder = getXmlFactoryFactory().newDocumentBuilder();
86fbe1
                 InputSource input = new InputSource(new StringReader(_data));
86fbe1
                 return new DOMSource(builder.parse(input));
86fbe1
             }
86fbe1
             else if (SAXSource.class.equals(sourceClass))
86fbe1
             {
86fbe1
+                XMLReader reader = getXmlFactoryFactory().createXMLReader();
86fbe1
                 InputSource is = new InputSource(new StringReader(_data));
86fbe1
-                return new SAXSource(is);
86fbe1
+                return new SAXSource(reader, is);
86fbe1
             }
86fbe1
             else if (StreamSource.class.equals(sourceClass))
86fbe1
             {
86fbe1
@@ -139,7 +145,7 @@ public class Jdbc4SQLXML implements SQLXML {
86fbe1
             }
86fbe1
             else if (StAXSource.class.equals(sourceClass))
86fbe1
             {
86fbe1
-                XMLInputFactory xif = XMLInputFactory.newInstance();
86fbe1
+                XMLInputFactory xif = getXmlFactoryFactory().newXMLInputFactory();
86fbe1
                 XMLStreamReader xsr = xif.createXMLStreamReader(new StringReader(_data));
86fbe1
                 return new StAXSource(xsr);
86fbe1
             }
86fbe1
@@ -185,7 +191,7 @@ public class Jdbc4SQLXML implements SQLXML {
86fbe1
             return _domResult;
86fbe1
         } else if (SAXResult.class.equals(resultClass)) {
86fbe1
             try {
86fbe1
-                SAXTransformerFactory transformerFactory = (SAXTransformerFactory)SAXTransformerFactory.newInstance();
86fbe1
+                SAXTransformerFactory transformerFactory = getXmlFactoryFactory().newSAXTransformerFactory();
86fbe1
                 TransformerHandler transformerHandler = transformerFactory.newTransformerHandler();
86fbe1
                 _stringWriter = new StringWriter();
86fbe1
                 transformerHandler.setResult(new StreamResult(_stringWriter));
86fbe1
@@ -201,7 +207,7 @@ public class Jdbc4SQLXML implements SQLXML {
86fbe1
         } else if (StAXResult.class.equals(resultClass)) {
86fbe1
             _stringWriter = new StringWriter();
86fbe1
             try {
86fbe1
-                XMLOutputFactory xof = XMLOutputFactory.newInstance();
86fbe1
+                XMLOutputFactory xof = getXmlFactoryFactory().newXMLOutputFactory();
86fbe1
                 XMLStreamWriter xsw = xof.createXMLStreamWriter(_stringWriter);
86fbe1
                 _active = true;
86fbe1
                 return new StAXResult(xsw);
86fbe1
@@ -258,7 +264,7 @@ public class Jdbc4SQLXML implements SQLXML {
86fbe1
             // and use the identify transform to get it into a
86fbe1
             // friendlier result format.
86fbe1
             try {
86fbe1
-                TransformerFactory factory = TransformerFactory.newInstance();
86fbe1
+                TransformerFactory factory = getXmlFactoryFactory().newTransformerFactory();
86fbe1
                 Transformer transformer = factory.newTransformer();
86fbe1
                 DOMSource domSource = new DOMSource(_domResult.getNode());
86fbe1
                 StringWriter stringWriter = new StringWriter();
86fbe1
@@ -284,14 +290,5 @@ public class Jdbc4SQLXML implements SQLXML {
86fbe1
         _initialized = true;
86fbe1
     }
86fbe1
 
86fbe1
-    // Don't clutter System.err with errors the user can't silence.
86fbe1
-    // If something bad really happens an exception will be thrown.
86fbe1
-    static class NonPrintingErrorHandler implements ErrorHandler
86fbe1
-    {
86fbe1
-        public void error(SAXParseException e) { }
86fbe1
-        public void fatalError(SAXParseException e) { }
86fbe1
-        public void warning(SAXParseException e) { }
86fbe1
-    }
86fbe1
-
86fbe1
 }
86fbe1
 
86fbe1
diff --git a/org/postgresql/xml/DefaultPGXmlFactoryFactory.java b/org/postgresql/xml/DefaultPGXmlFactoryFactory.java
86fbe1
new file mode 100644
86fbe1
index 0000000..7334935
86fbe1
--- /dev/null
86fbe1
+++ b/org/postgresql/xml/DefaultPGXmlFactoryFactory.java
86fbe1
@@ -0,0 +1,140 @@
86fbe1
+/*
86fbe1
+ * Copyright (c) 2020, PostgreSQL Global Development Group
86fbe1
+ * See the LICENSE file in the project root for more information.
86fbe1
+ */
86fbe1
+
86fbe1
+package org.postgresql.xml;
86fbe1
+
86fbe1
+import org.xml.sax.SAXException;
86fbe1
+import org.xml.sax.XMLReader;
86fbe1
+import org.xml.sax.helpers.XMLReaderFactory;
86fbe1
+
86fbe1
+import javax.xml.XMLConstants;
86fbe1
+import javax.xml.parsers.DocumentBuilder;
86fbe1
+import javax.xml.parsers.DocumentBuilderFactory;
86fbe1
+import javax.xml.parsers.ParserConfigurationException;
86fbe1
+import javax.xml.stream.XMLInputFactory;
86fbe1
+import javax.xml.stream.XMLOutputFactory;
86fbe1
+import javax.xml.transform.TransformerFactory;
86fbe1
+import javax.xml.transform.sax.SAXTransformerFactory;
86fbe1
+
86fbe1
+/**
86fbe1
+ * Default implementation of PGXmlFactoryFactory that configures each factory per OWASP recommendations.
86fbe1
+ *
86fbe1
+ * @see https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html
86fbe1
+ */
86fbe1
+public class DefaultPGXmlFactoryFactory implements PGXmlFactoryFactory {
86fbe1
+  public static final DefaultPGXmlFactoryFactory INSTANCE = new DefaultPGXmlFactoryFactory();
86fbe1
+
86fbe1
+  private DefaultPGXmlFactoryFactory() {
86fbe1
+  }
86fbe1
+
86fbe1
+  private DocumentBuilderFactory getDocumentBuilderFactory() {
86fbe1
+    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
86fbe1
+    setFactoryProperties(factory);
86fbe1
+    factory.setXIncludeAware(false);
86fbe1
+    factory.setExpandEntityReferences(false);
86fbe1
+    return factory;
86fbe1
+  }
86fbe1
+
86fbe1
+  @Override
86fbe1
+  public DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {
86fbe1
+    DocumentBuilder builder = getDocumentBuilderFactory().newDocumentBuilder();
86fbe1
+    builder.setEntityResolver(EmptyStringEntityResolver.INSTANCE);
86fbe1
+    builder.setErrorHandler(NullErrorHandler.INSTANCE);
86fbe1
+    return builder;
86fbe1
+  }
86fbe1
+
86fbe1
+  @Override
86fbe1
+  public TransformerFactory newTransformerFactory() {
86fbe1
+    TransformerFactory factory = TransformerFactory.newInstance();
86fbe1
+    setFactoryProperties(factory);
86fbe1
+    return factory;
86fbe1
+  }
86fbe1
+
86fbe1
+  @Override
86fbe1
+  public SAXTransformerFactory newSAXTransformerFactory() {
86fbe1
+    SAXTransformerFactory factory = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
86fbe1
+    setFactoryProperties(factory);
86fbe1
+    return factory;
86fbe1
+  }
86fbe1
+
86fbe1
+  @Override
86fbe1
+  public XMLInputFactory newXMLInputFactory() {
86fbe1
+    XMLInputFactory factory = XMLInputFactory.newInstance();
86fbe1
+    setPropertyQuietly(factory, XMLInputFactory.SUPPORT_DTD, false);
86fbe1
+    setPropertyQuietly(factory, XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
86fbe1
+    return factory;
86fbe1
+  }
86fbe1
+
86fbe1
+  @Override
86fbe1
+  public XMLOutputFactory newXMLOutputFactory() {
86fbe1
+    XMLOutputFactory factory = XMLOutputFactory.newInstance();
86fbe1
+    return factory;
86fbe1
+  }
86fbe1
+
86fbe1
+  @Override
86fbe1
+  public XMLReader createXMLReader() throws SAXException {
86fbe1
+    XMLReader factory = XMLReaderFactory.createXMLReader();
86fbe1
+    setFeatureQuietly(factory, "http://apache.org/xml/features/disallow-doctype-decl", true);
86fbe1
+    setFeatureQuietly(factory, "http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
86fbe1
+    setFeatureQuietly(factory, "http://xml.org/sax/features/external-general-entities", false);
86fbe1
+    setFeatureQuietly(factory, "http://xml.org/sax/features/external-parameter-entities", false);
86fbe1
+    factory.setErrorHandler(NullErrorHandler.INSTANCE);
86fbe1
+    return factory;
86fbe1
+  }
86fbe1
+
86fbe1
+  private static void setFeatureQuietly(Object factory, String name, boolean value) {
86fbe1
+    try {
86fbe1
+      if (factory instanceof DocumentBuilderFactory) {
86fbe1
+        ((DocumentBuilderFactory) factory).setFeature(name, value);
86fbe1
+      } else if (factory instanceof TransformerFactory) {
86fbe1
+        ((TransformerFactory) factory).setFeature(name, value);
86fbe1
+      } else if (factory instanceof XMLReader) {
86fbe1
+        ((XMLReader) factory).setFeature(name, value);
86fbe1
+      } else {
86fbe1
+        throw new Error("Invalid factory class: " + factory.getClass());
86fbe1
+      }
86fbe1
+      return;
86fbe1
+    } catch (Exception ignore) {
86fbe1
+    }
86fbe1
+  }
86fbe1
+
86fbe1
+  private static void setAttributeQuietly(Object factory, String name, Object value) {
86fbe1
+    try {
86fbe1
+      if (factory instanceof DocumentBuilderFactory) {
86fbe1
+        ((DocumentBuilderFactory) factory).setAttribute(name, value);
86fbe1
+      } else if (factory instanceof TransformerFactory) {
86fbe1
+        ((TransformerFactory) factory).setAttribute(name, value);
86fbe1
+      } else {
86fbe1
+        throw new Error("Invalid factory class: " + factory.getClass());
86fbe1
+      }
86fbe1
+    } catch (Exception ignore) {
86fbe1
+    }
86fbe1
+  }
86fbe1
+
86fbe1
+  private static void setFactoryProperties(Object factory) {
86fbe1
+    setFeatureQuietly(factory, XMLConstants.FEATURE_SECURE_PROCESSING, true);
86fbe1
+    setFeatureQuietly(factory, "http://apache.org/xml/features/disallow-doctype-decl", true);
86fbe1
+    setFeatureQuietly(factory, "http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
86fbe1
+    setFeatureQuietly(factory, "http://xml.org/sax/features/external-general-entities", false);
86fbe1
+    setFeatureQuietly(factory, "http://xml.org/sax/features/external-parameter-entities", false);
86fbe1
+    // Values from XMLConstants inlined for JDK 1.6 compatibility
86fbe1
+    setAttributeQuietly(factory, "http://javax.xml.XMLConstants/property/accessExternalDTD", "");
86fbe1
+    setAttributeQuietly(factory, "http://javax.xml.XMLConstants/property/accessExternalSchema", "");
86fbe1
+    setAttributeQuietly(factory, "http://javax.xml.XMLConstants/property/accessExternalStylesheet", "");
86fbe1
+  }
86fbe1
+
86fbe1
+  private static void setPropertyQuietly(Object factory, String name, Object value) {
86fbe1
+    try {
86fbe1
+      if (factory instanceof XMLReader) {
86fbe1
+        ((XMLReader) factory).setProperty(name, value);
86fbe1
+      } else if (factory instanceof XMLInputFactory) {
86fbe1
+        ((XMLInputFactory) factory).setProperty(name, value);
86fbe1
+      } else {
86fbe1
+        throw new Error("Invalid factory class: " + factory.getClass());
86fbe1
+      }
86fbe1
+    } catch (Exception ignore) {
86fbe1
+    }
86fbe1
+  }
86fbe1
+}
86fbe1
\ No newline at end of file
86fbe1
diff --git a/org/postgresql/xml/EmptyStringEntityResolver.java b/org/postgresql/xml/EmptyStringEntityResolver.java
86fbe1
new file mode 100644
86fbe1
index 0000000..e96a39b
86fbe1
--- /dev/null
86fbe1
+++ b/org/postgresql/xml/EmptyStringEntityResolver.java
86fbe1
@@ -0,0 +1,23 @@
86fbe1
+/*
86fbe1
+ * Copyright (c) 2020, PostgreSQL Global Development Group
86fbe1
+ * See the LICENSE file in the project root for more information.
86fbe1
+ */
86fbe1
+
86fbe1
+package org.postgresql.xml;
86fbe1
+
86fbe1
+import org.xml.sax.EntityResolver;
86fbe1
+import org.xml.sax.InputSource;
86fbe1
+import org.xml.sax.SAXException;
86fbe1
+
86fbe1
+import java.io.IOException;
86fbe1
+import java.io.StringReader;
86fbe1
+
86fbe1
+public class EmptyStringEntityResolver implements EntityResolver {
86fbe1
+  public static final EmptyStringEntityResolver INSTANCE = new EmptyStringEntityResolver();
86fbe1
+
86fbe1
+  @Override
86fbe1
+  public InputSource resolveEntity(String publicId, String systemId)
86fbe1
+      throws SAXException, IOException {
86fbe1
+    return new InputSource(new StringReader(""));
86fbe1
+  }
86fbe1
+}
86fbe1
diff --git a/org/postgresql/xml/LegacyInsecurePGXmlFactoryFactory.java b/org/postgresql/xml/LegacyInsecurePGXmlFactoryFactory.java
86fbe1
new file mode 100644
86fbe1
index 0000000..ed7a66b
86fbe1
--- /dev/null
86fbe1
+++ b/org/postgresql/xml/LegacyInsecurePGXmlFactoryFactory.java
86fbe1
@@ -0,0 +1,57 @@
86fbe1
+/*
86fbe1
+ * Copyright (c) 2020, PostgreSQL Global Development Group
86fbe1
+ * See the LICENSE file in the project root for more information.
86fbe1
+ */
86fbe1
+
86fbe1
+package org.postgresql.xml;
86fbe1
+
86fbe1
+import org.xml.sax.SAXException;
86fbe1
+import org.xml.sax.XMLReader;
86fbe1
+import org.xml.sax.helpers.XMLReaderFactory;
86fbe1
+
86fbe1
+import javax.xml.parsers.DocumentBuilder;
86fbe1
+import javax.xml.parsers.DocumentBuilderFactory;
86fbe1
+import javax.xml.parsers.ParserConfigurationException;
86fbe1
+import javax.xml.stream.XMLInputFactory;
86fbe1
+import javax.xml.stream.XMLOutputFactory;
86fbe1
+import javax.xml.transform.TransformerFactory;
86fbe1
+import javax.xml.transform.sax.SAXTransformerFactory;
86fbe1
+
86fbe1
+public class LegacyInsecurePGXmlFactoryFactory implements PGXmlFactoryFactory {
86fbe1
+  public static final LegacyInsecurePGXmlFactoryFactory INSTANCE = new LegacyInsecurePGXmlFactoryFactory();
86fbe1
+
86fbe1
+  private LegacyInsecurePGXmlFactoryFactory() {
86fbe1
+  }
86fbe1
+
86fbe1
+  @Override
86fbe1
+  public DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {
86fbe1
+    DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
86fbe1
+    builder.setErrorHandler(NullErrorHandler.INSTANCE);
86fbe1
+    return builder;
86fbe1
+  }
86fbe1
+
86fbe1
+  @Override
86fbe1
+  public TransformerFactory newTransformerFactory() {
86fbe1
+    return TransformerFactory.newInstance();
86fbe1
+  }
86fbe1
+
86fbe1
+  @Override
86fbe1
+  public SAXTransformerFactory newSAXTransformerFactory() {
86fbe1
+    return (SAXTransformerFactory) SAXTransformerFactory.newInstance();
86fbe1
+  }
86fbe1
+
86fbe1
+  @Override
86fbe1
+  public XMLInputFactory newXMLInputFactory() {
86fbe1
+    return XMLInputFactory.newInstance();
86fbe1
+  }
86fbe1
+
86fbe1
+  @Override
86fbe1
+  public XMLOutputFactory newXMLOutputFactory() {
86fbe1
+    return XMLOutputFactory.newInstance();
86fbe1
+  }
86fbe1
+
86fbe1
+  @Override
86fbe1
+  public XMLReader createXMLReader() throws SAXException {
86fbe1
+    return XMLReaderFactory.createXMLReader();
86fbe1
+  }
86fbe1
+}
86fbe1
\ No newline at end of file
86fbe1
diff --git a/org/postgresql/xml/NullErrorHandler.java b/org/postgresql/xml/NullErrorHandler.java
86fbe1
new file mode 100644
86fbe1
index 0000000..d12a4fa
86fbe1
--- /dev/null
86fbe1
+++ b/org/postgresql/xml/NullErrorHandler.java
86fbe1
@@ -0,0 +1,25 @@
86fbe1
+/*
86fbe1
+ * Copyright (c) 2020, PostgreSQL Global Development Group
86fbe1
+ * See the LICENSE file in the project root for more information.
86fbe1
+ */
86fbe1
+
86fbe1
+package org.postgresql.xml;
86fbe1
+
86fbe1
+import org.xml.sax.ErrorHandler;
86fbe1
+import org.xml.sax.SAXParseException;
86fbe1
+
86fbe1
+/**
86fbe1
+ * Error handler that silently suppresses all errors.
86fbe1
+ */
86fbe1
+public class NullErrorHandler implements ErrorHandler {
86fbe1
+  public static final NullErrorHandler INSTANCE = new NullErrorHandler();
86fbe1
+
86fbe1
+  public void error(SAXParseException e) {
86fbe1
+  }
86fbe1
+
86fbe1
+  public void fatalError(SAXParseException e) {
86fbe1
+  }
86fbe1
+
86fbe1
+  public void warning(SAXParseException e) {
86fbe1
+  }
86fbe1
+}
86fbe1
diff --git a/org/postgresql/xml/PGXmlFactoryFactory.java b/org/postgresql/xml/PGXmlFactoryFactory.java
86fbe1
new file mode 100644
86fbe1
index 0000000..4bb98e4
86fbe1
--- /dev/null
86fbe1
+++ b/org/postgresql/xml/PGXmlFactoryFactory.java
86fbe1
@@ -0,0 +1,30 @@
86fbe1
+/*
86fbe1
+ * Copyright (c) 2020, PostgreSQL Global Development Group
86fbe1
+ * See the LICENSE file in the project root for more information.
86fbe1
+ */
86fbe1
+
86fbe1
+package org.postgresql.xml;
86fbe1
+
86fbe1
+import org.xml.sax.SAXException;
86fbe1
+import org.xml.sax.XMLReader;
86fbe1
+
86fbe1
+import javax.xml.parsers.DocumentBuilder;
86fbe1
+import javax.xml.parsers.ParserConfigurationException;
86fbe1
+import javax.xml.stream.XMLInputFactory;
86fbe1
+import javax.xml.stream.XMLOutputFactory;
86fbe1
+import javax.xml.transform.TransformerFactory;
86fbe1
+import javax.xml.transform.sax.SAXTransformerFactory;
86fbe1
+
86fbe1
+public interface PGXmlFactoryFactory {
86fbe1
+  DocumentBuilder newDocumentBuilder() throws ParserConfigurationException;
86fbe1
+
86fbe1
+  TransformerFactory newTransformerFactory();
86fbe1
+
86fbe1
+  SAXTransformerFactory newSAXTransformerFactory();
86fbe1
+
86fbe1
+  XMLInputFactory newXMLInputFactory();
86fbe1
+
86fbe1
+  XMLOutputFactory newXMLOutputFactory();
86fbe1
+
86fbe1
+  XMLReader createXMLReader() throws SAXException;
86fbe1
+}
86fbe1
\ No newline at end of file
86fbe1
-- 
86fbe1
2.24.1
86fbe1