|
|
baebbc |
From 3b2a73ad85da069637a73beca432950204535979 Mon Sep 17 00:00:00 2001
|
|
|
baebbc |
From: Ondrej Dubaj <odubaj@redhat.com>
|
|
|
baebbc |
Date: Wed, 22 Jul 2020 11:39:42 +0200
|
|
|
baebbc |
Subject: [PATCH] Fix for XXE vulnerability
|
|
|
baebbc |
|
|
|
baebbc |
by defaulting to disabling external access and doc types. The
|
|
|
baebbc |
legacy insecure behavior can be restored via the new connection property xmlFactoryFactory
|
|
|
baebbc |
with a value of LEGACY_INSECURE. Alternatively, a custom class name can be specified that
|
|
|
baebbc |
implements org.postgresql.xml.PGXmlFactoryFactory and takes a no argument constructor.
|
|
|
baebbc |
|
|
|
baebbc |
* refactor: Clean up whitespace in existing PgSQLXMLTest
|
|
|
baebbc |
* fix: Fix XXE vulnerability in PgSQLXML by disabling external access and doctypes
|
|
|
baebbc |
* fix: Add missing getter and setter for XML_FACTORY_FACTORY to BasicDataSource
|
|
|
baebbc |
---
|
|
|
baebbc |
.../main/java/org/postgresql/PGProperty.java | 11 ++
|
|
|
baebbc |
.../org/postgresql/core/BaseConnection.java | 9 ++
|
|
|
baebbc |
.../postgresql/ds/common/BaseDataSource.java | 8 +
|
|
|
baebbc |
.../org/postgresql/jdbc/PgConnection.java | 41 +++++
|
|
|
baebbc |
.../java/org/postgresql/jdbc/PgSQLXML.java | 44 +++---
|
|
|
baebbc |
.../xml/DefaultPGXmlFactoryFactory.java | 141 ++++++++++++++++++
|
|
|
baebbc |
.../xml/EmptyStringEntityResolver.java | 23 +++
|
|
|
baebbc |
.../LegacyInsecurePGXmlFactoryFactory.java | 57 +++++++
|
|
|
baebbc |
.../org/postgresql/xml/NullErrorHandler.java | 25 ++++
|
|
|
baebbc |
.../postgresql/xml/PGXmlFactoryFactory.java | 30 ++++
|
|
|
baebbc |
.../org/postgresql/jdbc/PgSQLXMLTest.java | 124 +++++++++++++++
|
|
|
baebbc |
.../postgresql/test/jdbc2/Jdbc2TestSuite.java | 2 +
|
|
|
baebbc |
12 files changed, 489 insertions(+), 26 deletions(-)
|
|
|
baebbc |
create mode 100644 pgjdbc/src/main/java/org/postgresql/xml/DefaultPGXmlFactoryFactory.java
|
|
|
baebbc |
create mode 100644 pgjdbc/src/main/java/org/postgresql/xml/EmptyStringEntityResolver.java
|
|
|
baebbc |
create mode 100644 pgjdbc/src/main/java/org/postgresql/xml/LegacyInsecurePGXmlFactoryFactory.java
|
|
|
baebbc |
create mode 100644 pgjdbc/src/main/java/org/postgresql/xml/NullErrorHandler.java
|
|
|
baebbc |
create mode 100644 pgjdbc/src/main/java/org/postgresql/xml/PGXmlFactoryFactory.java
|
|
|
baebbc |
create mode 100644 pgjdbc/src/test/java/org/postgresql/jdbc/PgSQLXMLTest.java
|
|
|
baebbc |
|
|
|
baebbc |
diff --git a/pgjdbc/src/main/java/org/postgresql/PGProperty.java b/pgjdbc/src/main/java/org/postgresql/PGProperty.java
|
|
|
baebbc |
index e56e05e..7c2eed8 100644
|
|
|
baebbc |
--- a/pgjdbc/src/main/java/org/postgresql/PGProperty.java
|
|
|
baebbc |
+++ b/pgjdbc/src/main/java/org/postgresql/PGProperty.java
|
|
|
baebbc |
@@ -331,6 +331,17 @@ public enum PGProperty {
|
|
|
baebbc |
*/
|
|
|
baebbc |
USE_SPNEGO("useSpnego", "false", "Use SPNEGO in SSPI authentication requests"),
|
|
|
baebbc |
|
|
|
baebbc |
+ /**
|
|
|
baebbc |
+ * Factory class to instantiate factories for XML processing.
|
|
|
baebbc |
+ * The default factory disables external entity processing.
|
|
|
baebbc |
+ * Legacy behavior with external entity processing can be enabled by specifying a value of LEGACY_INSECURE.
|
|
|
baebbc |
+ * Or specify a custom class that implements {@code org.postgresql.xml.PGXmlFactoryFactory}.
|
|
|
baebbc |
+ */
|
|
|
baebbc |
+ XML_FACTORY_FACTORY(
|
|
|
baebbc |
+ "xmlFactoryFactory",
|
|
|
baebbc |
+ "",
|
|
|
baebbc |
+ "Factory class to instantiate factories for XML processing"),
|
|
|
baebbc |
+
|
|
|
baebbc |
/**
|
|
|
baebbc |
* Force one of
|
|
|
baebbc |
*
|
|
|
baebbc |
diff --git a/pgjdbc/src/main/java/org/postgresql/core/BaseConnection.java b/pgjdbc/src/main/java/org/postgresql/core/BaseConnection.java
|
|
|
baebbc |
index 1d316a0..5f85964 100644
|
|
|
baebbc |
--- a/pgjdbc/src/main/java/org/postgresql/core/BaseConnection.java
|
|
|
baebbc |
+++ b/pgjdbc/src/main/java/org/postgresql/core/BaseConnection.java
|
|
|
baebbc |
@@ -9,6 +9,7 @@ import org.postgresql.PGConnection;
|
|
|
baebbc |
import org.postgresql.jdbc.FieldMetadata;
|
|
|
baebbc |
import org.postgresql.jdbc.TimestampUtils;
|
|
|
baebbc |
import org.postgresql.util.LruCache;
|
|
|
baebbc |
+import org.postgresql.xml.PGXmlFactoryFactory;
|
|
|
baebbc |
|
|
|
baebbc |
import java.sql.Connection;
|
|
|
baebbc |
import java.sql.ResultSet;
|
|
|
baebbc |
@@ -202,4 +203,12 @@ public interface BaseConnection extends PGConnection, Connection {
|
|
|
baebbc |
* @param flushCacheOnDeallocate true if statement cache should be reset when "deallocate/discard" message observed
|
|
|
baebbc |
*/
|
|
|
baebbc |
void setFlushCacheOnDeallocate(boolean flushCacheOnDeallocate);
|
|
|
baebbc |
+
|
|
|
baebbc |
+ /**
|
|
|
baebbc |
+ * Retrieve the factory to instantiate XML processing factories.
|
|
|
baebbc |
+ *
|
|
|
baebbc |
+ * @return The factory to use to instantiate XML processing factories
|
|
|
baebbc |
+ * @throws SQLException if the class cannot be found or instantiated.
|
|
|
baebbc |
+ */
|
|
|
baebbc |
+ PGXmlFactoryFactory getXmlFactoryFactory() throws SQLException;
|
|
|
baebbc |
}
|
|
|
baebbc |
diff --git a/pgjdbc/src/main/java/org/postgresql/ds/common/BaseDataSource.java b/pgjdbc/src/main/java/org/postgresql/ds/common/BaseDataSource.java
|
|
|
baebbc |
index 268d936..2fb4e06 100644
|
|
|
baebbc |
--- a/pgjdbc/src/main/java/org/postgresql/ds/common/BaseDataSource.java
|
|
|
baebbc |
+++ b/pgjdbc/src/main/java/org/postgresql/ds/common/BaseDataSource.java
|
|
|
baebbc |
@@ -1313,4 +1313,12 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
|
|
|
baebbc |
return Logger.getLogger("org.postgresql");
|
|
|
baebbc |
}
|
|
|
baebbc |
//#endif
|
|
|
baebbc |
+
|
|
|
baebbc |
+ public String getXmlFactoryFactory() {
|
|
|
baebbc |
+ return PGProperty.XML_FACTORY_FACTORY.get(properties);
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+
|
|
|
baebbc |
+ public void setXmlFactoryFactory(String xmlFactoryFactory) {
|
|
|
baebbc |
+ PGProperty.XML_FACTORY_FACTORY.set(properties, xmlFactoryFactory);
|
|
|
baebbc |
+ }
|
|
|
baebbc |
}
|
|
|
baebbc |
diff --git a/pgjdbc/src/main/java/org/postgresql/jdbc/PgConnection.java b/pgjdbc/src/main/java/org/postgresql/jdbc/PgConnection.java
|
|
|
baebbc |
index 7140ab4..c9c4ada 100644
|
|
|
baebbc |
--- a/pgjdbc/src/main/java/org/postgresql/jdbc/PgConnection.java
|
|
|
baebbc |
+++ b/pgjdbc/src/main/java/org/postgresql/jdbc/PgConnection.java
|
|
|
baebbc |
@@ -37,6 +37,9 @@ import org.postgresql.util.PGBinaryObject;
|
|
|
baebbc |
import org.postgresql.util.PGobject;
|
|
|
baebbc |
import org.postgresql.util.PSQLException;
|
|
|
baebbc |
import org.postgresql.util.PSQLState;
|
|
|
baebbc |
+import org.postgresql.xml.DefaultPGXmlFactoryFactory;
|
|
|
baebbc |
+import org.postgresql.xml.LegacyInsecurePGXmlFactoryFactory;
|
|
|
baebbc |
+import org.postgresql.xml.PGXmlFactoryFactory;
|
|
|
baebbc |
|
|
|
baebbc |
import java.io.IOException;
|
|
|
baebbc |
import java.sql.Array;
|
|
|
baebbc |
@@ -142,6 +145,9 @@ public class PgConnection implements BaseConnection {
|
|
|
baebbc |
|
|
|
baebbc |
private final LruCache<FieldMetadata.Key, FieldMetadata> fieldMetadataCache;
|
|
|
baebbc |
|
|
|
baebbc |
+ private final String xmlFactoryFactoryClass;
|
|
|
baebbc |
+ private PGXmlFactoryFactory xmlFactoryFactory;
|
|
|
baebbc |
+
|
|
|
baebbc |
final CachedQuery borrowQuery(String sql) throws SQLException {
|
|
|
baebbc |
return queryExecutor.borrowQuery(sql);
|
|
|
baebbc |
}
|
|
|
baebbc |
@@ -290,6 +296,8 @@ public class PgConnection implements BaseConnection {
|
|
|
baebbc |
false);
|
|
|
baebbc |
|
|
|
baebbc |
replicationConnection = PGProperty.REPLICATION.get(info) != null;
|
|
|
baebbc |
+
|
|
|
baebbc |
+ xmlFactoryFactoryClass = PGProperty.XML_FACTORY_FACTORY.get(info);
|
|
|
baebbc |
}
|
|
|
baebbc |
|
|
|
baebbc |
private static Set<Integer> getBinaryOids(Properties info) throws PSQLException {
|
|
|
baebbc |
@@ -1729,4 +1737,37 @@ public class PgConnection implements BaseConnection {
|
|
|
baebbc |
}
|
|
|
baebbc |
return ps;
|
|
|
baebbc |
}
|
|
|
baebbc |
+
|
|
|
baebbc |
+ @Override
|
|
|
baebbc |
+ public PGXmlFactoryFactory getXmlFactoryFactory() throws SQLException {
|
|
|
baebbc |
+ if (xmlFactoryFactory == null) {
|
|
|
baebbc |
+ if (xmlFactoryFactoryClass == null || xmlFactoryFactoryClass.equals("")) {
|
|
|
baebbc |
+ xmlFactoryFactory = DefaultPGXmlFactoryFactory.INSTANCE;
|
|
|
baebbc |
+ } else if (xmlFactoryFactoryClass.equals("LEGACY_INSECURE")) {
|
|
|
baebbc |
+ xmlFactoryFactory = LegacyInsecurePGXmlFactoryFactory.INSTANCE;
|
|
|
baebbc |
+ } else {
|
|
|
baebbc |
+ Class clazz;
|
|
|
baebbc |
+ try {
|
|
|
baebbc |
+ clazz = Class.forName(xmlFactoryFactoryClass);
|
|
|
baebbc |
+ } catch (ClassNotFoundException ex) {
|
|
|
baebbc |
+ throw new PSQLException(
|
|
|
baebbc |
+ GT.tr("Could not instantiate xmlFactoryFactory: {0}", xmlFactoryFactoryClass),
|
|
|
baebbc |
+ PSQLState.INVALID_PARAMETER_VALUE, ex);
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+ if (!clazz.isAssignableFrom(PGXmlFactoryFactory.class)) {
|
|
|
baebbc |
+ throw new PSQLException(
|
|
|
baebbc |
+ GT.tr("Connection property xmlFactoryFactory must implement PGXmlFactoryFactory: {0}", xmlFactoryFactoryClass),
|
|
|
baebbc |
+ PSQLState.INVALID_PARAMETER_VALUE);
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+ try {
|
|
|
baebbc |
+ xmlFactoryFactory = (PGXmlFactoryFactory) clazz.newInstance();
|
|
|
baebbc |
+ } catch (Exception ex) {
|
|
|
baebbc |
+ throw new PSQLException(
|
|
|
baebbc |
+ GT.tr("Could not instantiate xmlFactoryFactory: {0}", xmlFactoryFactoryClass),
|
|
|
baebbc |
+ PSQLState.INVALID_PARAMETER_VALUE, ex);
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+ return xmlFactoryFactory;
|
|
|
baebbc |
+ }
|
|
|
baebbc |
}
|
|
|
baebbc |
diff --git a/pgjdbc/src/main/java/org/postgresql/jdbc/PgSQLXML.java b/pgjdbc/src/main/java/org/postgresql/jdbc/PgSQLXML.java
|
|
|
baebbc |
index 9fb0eed..dd7d5ac 100644
|
|
|
baebbc |
--- a/pgjdbc/src/main/java/org/postgresql/jdbc/PgSQLXML.java
|
|
|
baebbc |
+++ b/pgjdbc/src/main/java/org/postgresql/jdbc/PgSQLXML.java
|
|
|
baebbc |
@@ -9,10 +9,11 @@ import org.postgresql.core.BaseConnection;
|
|
|
baebbc |
import org.postgresql.util.GT;
|
|
|
baebbc |
import org.postgresql.util.PSQLException;
|
|
|
baebbc |
import org.postgresql.util.PSQLState;
|
|
|
baebbc |
+import org.postgresql.xml.DefaultPGXmlFactoryFactory;
|
|
|
baebbc |
+import org.postgresql.xml.PGXmlFactoryFactory;
|
|
|
baebbc |
|
|
|
baebbc |
-import org.xml.sax.ErrorHandler;
|
|
|
baebbc |
import org.xml.sax.InputSource;
|
|
|
baebbc |
-import org.xml.sax.SAXParseException;
|
|
|
baebbc |
+import org.xml.sax.XMLReader;
|
|
|
baebbc |
|
|
|
baebbc |
import java.io.ByteArrayInputStream;
|
|
|
baebbc |
import java.io.ByteArrayOutputStream;
|
|
|
baebbc |
@@ -27,7 +28,6 @@ import java.sql.SQLException;
|
|
|
baebbc |
import java.sql.SQLXML;
|
|
|
baebbc |
|
|
|
baebbc |
import javax.xml.parsers.DocumentBuilder;
|
|
|
baebbc |
-import javax.xml.parsers.DocumentBuilderFactory;
|
|
|
baebbc |
import javax.xml.stream.XMLInputFactory;
|
|
|
baebbc |
import javax.xml.stream.XMLOutputFactory;
|
|
|
baebbc |
import javax.xml.stream.XMLStreamException;
|
|
|
baebbc |
@@ -77,6 +77,13 @@ public class PgSQLXML implements SQLXML {
|
|
|
baebbc |
_freed = false;
|
|
|
baebbc |
}
|
|
|
baebbc |
|
|
|
baebbc |
+ private PGXmlFactoryFactory getXmlFactoryFactory() throws SQLException {
|
|
|
baebbc |
+ if (_conn != null) {
|
|
|
baebbc |
+ return _conn.getXmlFactoryFactory();
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+ return DefaultPGXmlFactoryFactory.INSTANCE;
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+
|
|
|
baebbc |
public synchronized void free() {
|
|
|
baebbc |
_freed = true;
|
|
|
baebbc |
_data = null;
|
|
|
baebbc |
@@ -128,18 +135,17 @@ public class PgSQLXML implements SQLXML {
|
|
|
baebbc |
|
|
|
baebbc |
try {
|
|
|
baebbc |
if (sourceClass == null || DOMSource.class.equals(sourceClass)) {
|
|
|
baebbc |
- DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
|
|
baebbc |
- DocumentBuilder builder = factory.newDocumentBuilder();
|
|
|
baebbc |
- builder.setErrorHandler(new NonPrintingErrorHandler());
|
|
|
baebbc |
+ DocumentBuilder builder = getXmlFactoryFactory().newDocumentBuilder();
|
|
|
baebbc |
InputSource input = new InputSource(new StringReader(_data));
|
|
|
baebbc |
return (T) new DOMSource(builder.parse(input));
|
|
|
baebbc |
} else if (SAXSource.class.equals(sourceClass)) {
|
|
|
baebbc |
+ XMLReader reader = getXmlFactoryFactory().createXMLReader();
|
|
|
baebbc |
InputSource is = new InputSource(new StringReader(_data));
|
|
|
baebbc |
- return (T) new SAXSource(is);
|
|
|
baebbc |
+ return (T) new SAXSource(reader, is);
|
|
|
baebbc |
} else if (StreamSource.class.equals(sourceClass)) {
|
|
|
baebbc |
return (T) new StreamSource(new StringReader(_data));
|
|
|
baebbc |
} else if (StAXSource.class.equals(sourceClass)) {
|
|
|
baebbc |
- XMLInputFactory xif = XMLInputFactory.newInstance();
|
|
|
baebbc |
+ XMLInputFactory xif = getXmlFactoryFactory().newXMLInputFactory();
|
|
|
baebbc |
XMLStreamReader xsr = xif.createXMLStreamReader(new StringReader(_data));
|
|
|
baebbc |
return (T) new StAXSource(xsr);
|
|
|
baebbc |
}
|
|
|
baebbc |
@@ -168,6 +174,7 @@ public class PgSQLXML implements SQLXML {
|
|
|
baebbc |
public synchronized Writer setCharacterStream() throws SQLException {
|
|
|
baebbc |
checkFreed();
|
|
|
baebbc |
initialize();
|
|
|
baebbc |
+ _active = true;
|
|
|
baebbc |
_stringWriter = new StringWriter();
|
|
|
baebbc |
return _stringWriter;
|
|
|
baebbc |
}
|
|
|
baebbc |
@@ -182,8 +189,7 @@ public class PgSQLXML implements SQLXML {
|
|
|
baebbc |
return (T) _domResult;
|
|
|
baebbc |
} else if (SAXResult.class.equals(resultClass)) {
|
|
|
baebbc |
try {
|
|
|
baebbc |
- SAXTransformerFactory transformerFactory =
|
|
|
baebbc |
- (SAXTransformerFactory) SAXTransformerFactory.newInstance();
|
|
|
baebbc |
+ SAXTransformerFactory transformerFactory = getXmlFactoryFactory().newSAXTransformerFactory();
|
|
|
baebbc |
TransformerHandler transformerHandler = transformerFactory.newTransformerHandler();
|
|
|
baebbc |
_stringWriter = new StringWriter();
|
|
|
baebbc |
transformerHandler.setResult(new StreamResult(_stringWriter));
|
|
|
baebbc |
@@ -200,7 +206,7 @@ public class PgSQLXML implements SQLXML {
|
|
|
baebbc |
} else if (StAXResult.class.equals(resultClass)) {
|
|
|
baebbc |
_stringWriter = new StringWriter();
|
|
|
baebbc |
try {
|
|
|
baebbc |
- XMLOutputFactory xof = XMLOutputFactory.newInstance();
|
|
|
baebbc |
+ XMLOutputFactory xof = getXmlFactoryFactory().newXMLOutputFactory();
|
|
|
baebbc |
XMLStreamWriter xsw = xof.createXMLStreamWriter(_stringWriter);
|
|
|
baebbc |
_active = true;
|
|
|
baebbc |
return (T) new StAXResult(xsw);
|
|
|
baebbc |
@@ -262,7 +268,7 @@ public class PgSQLXML implements SQLXML {
|
|
|
baebbc |
// and use the identify transform to get it into a
|
|
|
baebbc |
// friendlier result format.
|
|
|
baebbc |
try {
|
|
|
baebbc |
- TransformerFactory factory = TransformerFactory.newInstance();
|
|
|
baebbc |
+ TransformerFactory factory = getXmlFactoryFactory().newTransformerFactory();
|
|
|
baebbc |
Transformer transformer = factory.newTransformer();
|
|
|
baebbc |
DOMSource domSource = new DOMSource(_domResult.getNode());
|
|
|
baebbc |
StringWriter stringWriter = new StringWriter();
|
|
|
baebbc |
@@ -289,19 +295,5 @@ public class PgSQLXML implements SQLXML {
|
|
|
baebbc |
}
|
|
|
baebbc |
_initialized = true;
|
|
|
baebbc |
}
|
|
|
baebbc |
-
|
|
|
baebbc |
- // Don't clutter System.err with errors the user can't silence.
|
|
|
baebbc |
- // If something bad really happens an exception will be thrown.
|
|
|
baebbc |
- static class NonPrintingErrorHandler implements ErrorHandler {
|
|
|
baebbc |
- public void error(SAXParseException e) {
|
|
|
baebbc |
- }
|
|
|
baebbc |
-
|
|
|
baebbc |
- public void fatalError(SAXParseException e) {
|
|
|
baebbc |
- }
|
|
|
baebbc |
-
|
|
|
baebbc |
- public void warning(SAXParseException e) {
|
|
|
baebbc |
- }
|
|
|
baebbc |
- }
|
|
|
baebbc |
-
|
|
|
baebbc |
}
|
|
|
baebbc |
|
|
|
baebbc |
diff --git a/pgjdbc/src/main/java/org/postgresql/xml/DefaultPGXmlFactoryFactory.java b/pgjdbc/src/main/java/org/postgresql/xml/DefaultPGXmlFactoryFactory.java
|
|
|
baebbc |
new file mode 100644
|
|
|
baebbc |
index 0000000..b6a381d
|
|
|
baebbc |
--- /dev/null
|
|
|
baebbc |
+++ b/pgjdbc/src/main/java/org/postgresql/xml/DefaultPGXmlFactoryFactory.java
|
|
|
baebbc |
@@ -0,0 +1,141 @@
|
|
|
baebbc |
+
|
|
|
baebbc |
+/*
|
|
|
baebbc |
+ * Copyright (c) 2020, PostgreSQL Global Development Group
|
|
|
baebbc |
+ * See the LICENSE file in the project root for more information.
|
|
|
baebbc |
+ */
|
|
|
baebbc |
+
|
|
|
baebbc |
+package org.postgresql.xml;
|
|
|
baebbc |
+
|
|
|
baebbc |
+import org.xml.sax.SAXException;
|
|
|
baebbc |
+import org.xml.sax.XMLReader;
|
|
|
baebbc |
+import org.xml.sax.helpers.XMLReaderFactory;
|
|
|
baebbc |
+
|
|
|
baebbc |
+import javax.xml.XMLConstants;
|
|
|
baebbc |
+import javax.xml.parsers.DocumentBuilder;
|
|
|
baebbc |
+import javax.xml.parsers.DocumentBuilderFactory;
|
|
|
baebbc |
+import javax.xml.parsers.ParserConfigurationException;
|
|
|
baebbc |
+import javax.xml.stream.XMLInputFactory;
|
|
|
baebbc |
+import javax.xml.stream.XMLOutputFactory;
|
|
|
baebbc |
+import javax.xml.transform.TransformerFactory;
|
|
|
baebbc |
+import javax.xml.transform.sax.SAXTransformerFactory;
|
|
|
baebbc |
+
|
|
|
baebbc |
+/**
|
|
|
baebbc |
+ * Default implementation of PGXmlFactoryFactory that configures each factory per OWASP recommendations.
|
|
|
baebbc |
+ *
|
|
|
baebbc |
+ * @see https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html
|
|
|
baebbc |
+ */
|
|
|
baebbc |
+public class DefaultPGXmlFactoryFactory implements PGXmlFactoryFactory {
|
|
|
baebbc |
+ public static final DefaultPGXmlFactoryFactory INSTANCE = new DefaultPGXmlFactoryFactory();
|
|
|
baebbc |
+
|
|
|
baebbc |
+ private DefaultPGXmlFactoryFactory() {
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+
|
|
|
baebbc |
+ private DocumentBuilderFactory getDocumentBuilderFactory() {
|
|
|
baebbc |
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
|
|
baebbc |
+ setFactoryProperties(factory);
|
|
|
baebbc |
+ factory.setXIncludeAware(false);
|
|
|
baebbc |
+ factory.setExpandEntityReferences(false);
|
|
|
baebbc |
+ return factory;
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+
|
|
|
baebbc |
+ @Override
|
|
|
baebbc |
+ public DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {
|
|
|
baebbc |
+ DocumentBuilder builder = getDocumentBuilderFactory().newDocumentBuilder();
|
|
|
baebbc |
+ builder.setEntityResolver(EmptyStringEntityResolver.INSTANCE);
|
|
|
baebbc |
+ builder.setErrorHandler(NullErrorHandler.INSTANCE);
|
|
|
baebbc |
+ return builder;
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+
|
|
|
baebbc |
+ @Override
|
|
|
baebbc |
+ public TransformerFactory newTransformerFactory() {
|
|
|
baebbc |
+ TransformerFactory factory = TransformerFactory.newInstance();
|
|
|
baebbc |
+ setFactoryProperties(factory);
|
|
|
baebbc |
+ return factory;
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+
|
|
|
baebbc |
+ @Override
|
|
|
baebbc |
+ public SAXTransformerFactory newSAXTransformerFactory() {
|
|
|
baebbc |
+ SAXTransformerFactory factory = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
|
|
|
baebbc |
+ setFactoryProperties(factory);
|
|
|
baebbc |
+ return factory;
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+
|
|
|
baebbc |
+ @Override
|
|
|
baebbc |
+ public XMLInputFactory newXMLInputFactory() {
|
|
|
baebbc |
+ XMLInputFactory factory = XMLInputFactory.newInstance();
|
|
|
baebbc |
+ setPropertyQuietly(factory, XMLInputFactory.SUPPORT_DTD, false);
|
|
|
baebbc |
+ setPropertyQuietly(factory, XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
|
|
|
baebbc |
+ return factory;
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+
|
|
|
baebbc |
+ @Override
|
|
|
baebbc |
+ public XMLOutputFactory newXMLOutputFactory() {
|
|
|
baebbc |
+ XMLOutputFactory factory = XMLOutputFactory.newInstance();
|
|
|
baebbc |
+ return factory;
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+
|
|
|
baebbc |
+ @Override
|
|
|
baebbc |
+ public XMLReader createXMLReader() throws SAXException {
|
|
|
baebbc |
+ XMLReader factory = XMLReaderFactory.createXMLReader();
|
|
|
baebbc |
+ setFeatureQuietly(factory, "http://apache.org/xml/features/disallow-doctype-decl", true);
|
|
|
baebbc |
+ setFeatureQuietly(factory, "http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
|
|
|
baebbc |
+ setFeatureQuietly(factory, "http://xml.org/sax/features/external-general-entities", false);
|
|
|
baebbc |
+ setFeatureQuietly(factory, "http://xml.org/sax/features/external-parameter-entities", false);
|
|
|
baebbc |
+ factory.setErrorHandler(NullErrorHandler.INSTANCE);
|
|
|
baebbc |
+ return factory;
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+
|
|
|
baebbc |
+ private static void setFeatureQuietly(Object factory, String name, boolean value) {
|
|
|
baebbc |
+ try {
|
|
|
baebbc |
+ if (factory instanceof DocumentBuilderFactory) {
|
|
|
baebbc |
+ ((DocumentBuilderFactory) factory).setFeature(name, value);
|
|
|
baebbc |
+ } else if (factory instanceof TransformerFactory) {
|
|
|
baebbc |
+ ((TransformerFactory) factory).setFeature(name, value);
|
|
|
baebbc |
+ } else if (factory instanceof XMLReader) {
|
|
|
baebbc |
+ ((XMLReader) factory).setFeature(name, value);
|
|
|
baebbc |
+ } else {
|
|
|
baebbc |
+ throw new Error("Invalid factory class: " + factory.getClass());
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+ return;
|
|
|
baebbc |
+ } catch (Exception ignore) {
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+
|
|
|
baebbc |
+ private static void setAttributeQuietly(Object factory, String name, Object value) {
|
|
|
baebbc |
+ try {
|
|
|
baebbc |
+ if (factory instanceof DocumentBuilderFactory) {
|
|
|
baebbc |
+ ((DocumentBuilderFactory) factory).setAttribute(name, value);
|
|
|
baebbc |
+ } else if (factory instanceof TransformerFactory) {
|
|
|
baebbc |
+ ((TransformerFactory) factory).setAttribute(name, value);
|
|
|
baebbc |
+ } else {
|
|
|
baebbc |
+ throw new Error("Invalid factory class: " + factory.getClass());
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+ } catch (Exception ignore) {
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+
|
|
|
baebbc |
+ private static void setFactoryProperties(Object factory) {
|
|
|
baebbc |
+ setFeatureQuietly(factory, XMLConstants.FEATURE_SECURE_PROCESSING, true);
|
|
|
baebbc |
+ setFeatureQuietly(factory, "http://apache.org/xml/features/disallow-doctype-decl", true);
|
|
|
baebbc |
+ setFeatureQuietly(factory, "http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
|
|
|
baebbc |
+ setFeatureQuietly(factory, "http://xml.org/sax/features/external-general-entities", false);
|
|
|
baebbc |
+ setFeatureQuietly(factory, "http://xml.org/sax/features/external-parameter-entities", false);
|
|
|
baebbc |
+ // Values from XMLConstants inlined for JDK 1.6 compatibility
|
|
|
baebbc |
+ setAttributeQuietly(factory, "http://javax.xml.XMLConstants/property/accessExternalDTD", "");
|
|
|
baebbc |
+ setAttributeQuietly(factory, "http://javax.xml.XMLConstants/property/accessExternalSchema", "");
|
|
|
baebbc |
+ setAttributeQuietly(factory, "http://javax.xml.XMLConstants/property/accessExternalStylesheet", "");
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+
|
|
|
baebbc |
+ private static void setPropertyQuietly(Object factory, String name, Object value) {
|
|
|
baebbc |
+ try {
|
|
|
baebbc |
+ if (factory instanceof XMLReader) {
|
|
|
baebbc |
+ ((XMLReader) factory).setProperty(name, value);
|
|
|
baebbc |
+ } else if (factory instanceof XMLInputFactory) {
|
|
|
baebbc |
+ ((XMLInputFactory) factory).setProperty(name, value);
|
|
|
baebbc |
+ } else {
|
|
|
baebbc |
+ throw new Error("Invalid factory class: " + factory.getClass());
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+ } catch (Exception ignore) {
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+}
|
|
|
baebbc |
\ No newline at end of file
|
|
|
baebbc |
diff --git a/pgjdbc/src/main/java/org/postgresql/xml/EmptyStringEntityResolver.java b/pgjdbc/src/main/java/org/postgresql/xml/EmptyStringEntityResolver.java
|
|
|
baebbc |
new file mode 100644
|
|
|
baebbc |
index 0000000..39227e0
|
|
|
baebbc |
--- /dev/null
|
|
|
baebbc |
+++ b/pgjdbc/src/main/java/org/postgresql/xml/EmptyStringEntityResolver.java
|
|
|
baebbc |
@@ -0,0 +1,23 @@
|
|
|
baebbc |
+/*
|
|
|
baebbc |
+ * Copyright (c) 2020, PostgreSQL Global Development Group
|
|
|
baebbc |
+ * See the LICENSE file in the project root for more information.
|
|
|
baebbc |
+ */
|
|
|
baebbc |
+
|
|
|
baebbc |
+package org.postgresql.xml;
|
|
|
baebbc |
+
|
|
|
baebbc |
+import org.xml.sax.EntityResolver;
|
|
|
baebbc |
+import org.xml.sax.InputSource;
|
|
|
baebbc |
+import org.xml.sax.SAXException;
|
|
|
baebbc |
+
|
|
|
baebbc |
+import java.io.IOException;
|
|
|
baebbc |
+import java.io.StringReader;
|
|
|
baebbc |
+
|
|
|
baebbc |
+public class EmptyStringEntityResolver implements EntityResolver {
|
|
|
baebbc |
+ public static final EmptyStringEntityResolver INSTANCE = new EmptyStringEntityResolver();
|
|
|
baebbc |
+
|
|
|
baebbc |
+ @Override
|
|
|
baebbc |
+ public InputSource resolveEntity(String publicId, String systemId)
|
|
|
baebbc |
+ throws SAXException, IOException {
|
|
|
baebbc |
+ return new InputSource(new StringReader(""));
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+}
|
|
|
baebbc |
\ No newline at end of file
|
|
|
baebbc |
diff --git a/pgjdbc/src/main/java/org/postgresql/xml/LegacyInsecurePGXmlFactoryFactory.java b/pgjdbc/src/main/java/org/postgresql/xml/LegacyInsecurePGXmlFactoryFactory.java
|
|
|
baebbc |
new file mode 100644
|
|
|
baebbc |
index 0000000..ed7a66b
|
|
|
baebbc |
--- /dev/null
|
|
|
baebbc |
+++ b/pgjdbc/src/main/java/org/postgresql/xml/LegacyInsecurePGXmlFactoryFactory.java
|
|
|
baebbc |
@@ -0,0 +1,57 @@
|
|
|
baebbc |
+/*
|
|
|
baebbc |
+ * Copyright (c) 2020, PostgreSQL Global Development Group
|
|
|
baebbc |
+ * See the LICENSE file in the project root for more information.
|
|
|
baebbc |
+ */
|
|
|
baebbc |
+
|
|
|
baebbc |
+package org.postgresql.xml;
|
|
|
baebbc |
+
|
|
|
baebbc |
+import org.xml.sax.SAXException;
|
|
|
baebbc |
+import org.xml.sax.XMLReader;
|
|
|
baebbc |
+import org.xml.sax.helpers.XMLReaderFactory;
|
|
|
baebbc |
+
|
|
|
baebbc |
+import javax.xml.parsers.DocumentBuilder;
|
|
|
baebbc |
+import javax.xml.parsers.DocumentBuilderFactory;
|
|
|
baebbc |
+import javax.xml.parsers.ParserConfigurationException;
|
|
|
baebbc |
+import javax.xml.stream.XMLInputFactory;
|
|
|
baebbc |
+import javax.xml.stream.XMLOutputFactory;
|
|
|
baebbc |
+import javax.xml.transform.TransformerFactory;
|
|
|
baebbc |
+import javax.xml.transform.sax.SAXTransformerFactory;
|
|
|
baebbc |
+
|
|
|
baebbc |
+public class LegacyInsecurePGXmlFactoryFactory implements PGXmlFactoryFactory {
|
|
|
baebbc |
+ public static final LegacyInsecurePGXmlFactoryFactory INSTANCE = new LegacyInsecurePGXmlFactoryFactory();
|
|
|
baebbc |
+
|
|
|
baebbc |
+ private LegacyInsecurePGXmlFactoryFactory() {
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+
|
|
|
baebbc |
+ @Override
|
|
|
baebbc |
+ public DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {
|
|
|
baebbc |
+ DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
|
|
|
baebbc |
+ builder.setErrorHandler(NullErrorHandler.INSTANCE);
|
|
|
baebbc |
+ return builder;
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+
|
|
|
baebbc |
+ @Override
|
|
|
baebbc |
+ public TransformerFactory newTransformerFactory() {
|
|
|
baebbc |
+ return TransformerFactory.newInstance();
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+
|
|
|
baebbc |
+ @Override
|
|
|
baebbc |
+ public SAXTransformerFactory newSAXTransformerFactory() {
|
|
|
baebbc |
+ return (SAXTransformerFactory) SAXTransformerFactory.newInstance();
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+
|
|
|
baebbc |
+ @Override
|
|
|
baebbc |
+ public XMLInputFactory newXMLInputFactory() {
|
|
|
baebbc |
+ return XMLInputFactory.newInstance();
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+
|
|
|
baebbc |
+ @Override
|
|
|
baebbc |
+ public XMLOutputFactory newXMLOutputFactory() {
|
|
|
baebbc |
+ return XMLOutputFactory.newInstance();
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+
|
|
|
baebbc |
+ @Override
|
|
|
baebbc |
+ public XMLReader createXMLReader() throws SAXException {
|
|
|
baebbc |
+ return XMLReaderFactory.createXMLReader();
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+}
|
|
|
baebbc |
\ No newline at end of file
|
|
|
baebbc |
diff --git a/pgjdbc/src/main/java/org/postgresql/xml/NullErrorHandler.java b/pgjdbc/src/main/java/org/postgresql/xml/NullErrorHandler.java
|
|
|
baebbc |
new file mode 100644
|
|
|
baebbc |
index 0000000..ad486c7
|
|
|
baebbc |
--- /dev/null
|
|
|
baebbc |
+++ b/pgjdbc/src/main/java/org/postgresql/xml/NullErrorHandler.java
|
|
|
baebbc |
@@ -0,0 +1,25 @@
|
|
|
baebbc |
+/*
|
|
|
baebbc |
+ * Copyright (c) 2020, PostgreSQL Global Development Group
|
|
|
baebbc |
+ * See the LICENSE file in the project root for more information.
|
|
|
baebbc |
+ */
|
|
|
baebbc |
+
|
|
|
baebbc |
+package org.postgresql.xml;
|
|
|
baebbc |
+
|
|
|
baebbc |
+import org.xml.sax.ErrorHandler;
|
|
|
baebbc |
+import org.xml.sax.SAXParseException;
|
|
|
baebbc |
+
|
|
|
baebbc |
+/**
|
|
|
baebbc |
+ * Error handler that silently suppresses all errors.
|
|
|
baebbc |
+ */
|
|
|
baebbc |
+public class NullErrorHandler implements ErrorHandler {
|
|
|
baebbc |
+ public static final NullErrorHandler INSTANCE = new NullErrorHandler();
|
|
|
baebbc |
+
|
|
|
baebbc |
+ public void error(SAXParseException e) {
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+
|
|
|
baebbc |
+ public void fatalError(SAXParseException e) {
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+
|
|
|
baebbc |
+ public void warning(SAXParseException e) {
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+}
|
|
|
baebbc |
\ No newline at end of file
|
|
|
baebbc |
diff --git a/pgjdbc/src/main/java/org/postgresql/xml/PGXmlFactoryFactory.java b/pgjdbc/src/main/java/org/postgresql/xml/PGXmlFactoryFactory.java
|
|
|
baebbc |
new file mode 100644
|
|
|
baebbc |
index 0000000..4bb98e4
|
|
|
baebbc |
--- /dev/null
|
|
|
baebbc |
+++ b/pgjdbc/src/main/java/org/postgresql/xml/PGXmlFactoryFactory.java
|
|
|
baebbc |
@@ -0,0 +1,30 @@
|
|
|
baebbc |
+/*
|
|
|
baebbc |
+ * Copyright (c) 2020, PostgreSQL Global Development Group
|
|
|
baebbc |
+ * See the LICENSE file in the project root for more information.
|
|
|
baebbc |
+ */
|
|
|
baebbc |
+
|
|
|
baebbc |
+package org.postgresql.xml;
|
|
|
baebbc |
+
|
|
|
baebbc |
+import org.xml.sax.SAXException;
|
|
|
baebbc |
+import org.xml.sax.XMLReader;
|
|
|
baebbc |
+
|
|
|
baebbc |
+import javax.xml.parsers.DocumentBuilder;
|
|
|
baebbc |
+import javax.xml.parsers.ParserConfigurationException;
|
|
|
baebbc |
+import javax.xml.stream.XMLInputFactory;
|
|
|
baebbc |
+import javax.xml.stream.XMLOutputFactory;
|
|
|
baebbc |
+import javax.xml.transform.TransformerFactory;
|
|
|
baebbc |
+import javax.xml.transform.sax.SAXTransformerFactory;
|
|
|
baebbc |
+
|
|
|
baebbc |
+public interface PGXmlFactoryFactory {
|
|
|
baebbc |
+ DocumentBuilder newDocumentBuilder() throws ParserConfigurationException;
|
|
|
baebbc |
+
|
|
|
baebbc |
+ TransformerFactory newTransformerFactory();
|
|
|
baebbc |
+
|
|
|
baebbc |
+ SAXTransformerFactory newSAXTransformerFactory();
|
|
|
baebbc |
+
|
|
|
baebbc |
+ XMLInputFactory newXMLInputFactory();
|
|
|
baebbc |
+
|
|
|
baebbc |
+ XMLOutputFactory newXMLOutputFactory();
|
|
|
baebbc |
+
|
|
|
baebbc |
+ XMLReader createXMLReader() throws SAXException;
|
|
|
baebbc |
+}
|
|
|
baebbc |
\ No newline at end of file
|
|
|
baebbc |
diff --git a/pgjdbc/src/test/java/org/postgresql/jdbc/PgSQLXMLTest.java b/pgjdbc/src/test/java/org/postgresql/jdbc/PgSQLXMLTest.java
|
|
|
baebbc |
new file mode 100644
|
|
|
baebbc |
index 0000000..49e389c
|
|
|
baebbc |
--- /dev/null
|
|
|
baebbc |
+++ b/pgjdbc/src/test/java/org/postgresql/jdbc/PgSQLXMLTest.java
|
|
|
baebbc |
@@ -0,0 +1,124 @@
|
|
|
baebbc |
+/*
|
|
|
baebbc |
+ * Copyright (c) 2019, PostgreSQL Global Development Group
|
|
|
baebbc |
+ * See the LICENSE file in the project root for more information.
|
|
|
baebbc |
+ */
|
|
|
baebbc |
+
|
|
|
baebbc |
+package org.postgresql.jdbc;
|
|
|
baebbc |
+
|
|
|
baebbc |
+import static org.junit.Assert.assertEquals;
|
|
|
baebbc |
+import static org.junit.Assert.assertNotNull;
|
|
|
baebbc |
+import static org.junit.Assert.assertTrue;
|
|
|
baebbc |
+import static org.junit.Assert.fail;
|
|
|
baebbc |
+
|
|
|
baebbc |
+import org.postgresql.PGProperty;
|
|
|
baebbc |
+import org.postgresql.core.BaseConnection;
|
|
|
baebbc |
+import org.postgresql.test.TestUtil;
|
|
|
baebbc |
+import org.postgresql.test.jdbc2.BaseTest4;
|
|
|
baebbc |
+
|
|
|
baebbc |
+import org.junit.Before;
|
|
|
baebbc |
+import org.junit.Test;
|
|
|
baebbc |
+
|
|
|
baebbc |
+import java.io.StringWriter;
|
|
|
baebbc |
+import java.io.Writer;
|
|
|
baebbc |
+import java.sql.Connection;
|
|
|
baebbc |
+import java.sql.PreparedStatement;
|
|
|
baebbc |
+import java.sql.ResultSet;
|
|
|
baebbc |
+import java.sql.SQLException;
|
|
|
baebbc |
+import java.sql.SQLXML;
|
|
|
baebbc |
+import java.sql.Statement;
|
|
|
baebbc |
+import java.util.Properties;
|
|
|
baebbc |
+
|
|
|
baebbc |
+import javax.xml.stream.XMLStreamException;
|
|
|
baebbc |
+import javax.xml.stream.XMLStreamReader;
|
|
|
baebbc |
+import javax.xml.transform.Source;
|
|
|
baebbc |
+import javax.xml.transform.Transformer;
|
|
|
baebbc |
+import javax.xml.transform.TransformerException;
|
|
|
baebbc |
+import javax.xml.transform.TransformerFactory;
|
|
|
baebbc |
+import javax.xml.transform.dom.DOMSource;
|
|
|
baebbc |
+import javax.xml.transform.sax.SAXSource;
|
|
|
baebbc |
+import javax.xml.transform.stax.StAXSource;
|
|
|
baebbc |
+import javax.xml.transform.stream.StreamResult;
|
|
|
baebbc |
+
|
|
|
baebbc |
+public class PgSQLXMLTest extends BaseTest4 {
|
|
|
baebbc |
+
|
|
|
baebbc |
+ @Override
|
|
|
baebbc |
+ @Before
|
|
|
baebbc |
+ public void setUp() throws Exception {
|
|
|
baebbc |
+ super.setUp();
|
|
|
baebbc |
+ TestUtil.createTempTable(con, "xmltab", "x xml");
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+
|
|
|
baebbc |
+ @Test
|
|
|
baebbc |
+ public void setCharacterStream() throws Exception {
|
|
|
baebbc |
+ String exmplar = "<x>value</x>";
|
|
|
baebbc |
+ SQLXML pgSQLXML = con.createSQLXML();
|
|
|
baebbc |
+ Writer writer = pgSQLXML.setCharacterStream();
|
|
|
baebbc |
+ writer.write(exmplar);
|
|
|
baebbc |
+ PreparedStatement preparedStatement = con.prepareStatement("insert into xmltab values (?)");
|
|
|
baebbc |
+ preparedStatement.setSQLXML(1, pgSQLXML);
|
|
|
baebbc |
+ preparedStatement.execute();
|
|
|
baebbc |
+
|
|
|
baebbc |
+ Statement statement = con.createStatement();
|
|
|
baebbc |
+ ResultSet rs = statement.executeQuery("select * from xmltab");
|
|
|
baebbc |
+ assertTrue(rs.next());
|
|
|
baebbc |
+ SQLXML result = rs.getSQLXML(1);
|
|
|
baebbc |
+ assertNotNull(result);
|
|
|
baebbc |
+ assertEquals(exmplar, result.getString());
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+
|
|
|
baebbc |
+ private static final String LICENSE_URL =
|
|
|
baebbc |
+ PgSQLXMLTest.class.getClassLoader().getResource("META-INF/LICENSE").toString();
|
|
|
baebbc |
+ private static final String XXE_EXAMPLE =
|
|
|
baebbc |
+ "\n"
|
|
|
baebbc |
+ + "]>"
|
|
|
baebbc |
+ + "<foo>&xx;;</foo>";
|
|
|
baebbc |
+
|
|
|
baebbc |
+ @Test
|
|
|
baebbc |
+ public void testLegacyXxe() throws Exception {
|
|
|
baebbc |
+ Properties props = new Properties();
|
|
|
baebbc |
+ props.setProperty(PGProperty.XML_FACTORY_FACTORY.getName(), "LEGACY_INSECURE");
|
|
|
baebbc |
+ try (Connection conn = TestUtil.openDB(props)) {
|
|
|
baebbc |
+ BaseConnection baseConn = conn.unwrap(BaseConnection.class);
|
|
|
baebbc |
+ PgSQLXML xml = new PgSQLXML(baseConn, XXE_EXAMPLE);
|
|
|
baebbc |
+ xml.getSource(null);
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+
|
|
|
baebbc |
+ private static String sourceToString(Source source) throws TransformerException {
|
|
|
baebbc |
+ StringWriter sw = new StringWriter();
|
|
|
baebbc |
+ Transformer transformer = TransformerFactory.newInstance().newTransformer();
|
|
|
baebbc |
+ transformer.transform(source, new StreamResult(sw));
|
|
|
baebbc |
+ return sw.toString();
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+
|
|
|
baebbc |
+ @Test(expected = SQLException.class)
|
|
|
baebbc |
+ public void testGetSourceXxeNull() throws Exception {
|
|
|
baebbc |
+ PgSQLXML xml = new PgSQLXML(null, XXE_EXAMPLE);
|
|
|
baebbc |
+ xml.getSource(null);
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+
|
|
|
baebbc |
+ @Test(expected = SQLException.class)
|
|
|
baebbc |
+ public void testGetSourceXxeDOMSource() throws Exception {
|
|
|
baebbc |
+ PgSQLXML xml = new PgSQLXML(null, XXE_EXAMPLE);
|
|
|
baebbc |
+ xml.getSource(DOMSource.class);
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+
|
|
|
baebbc |
+ @Test(expected = TransformerException.class)
|
|
|
baebbc |
+ public void testGetSourceXxeSAXSource() throws Exception {
|
|
|
baebbc |
+ PgSQLXML xml = new PgSQLXML(null, XXE_EXAMPLE);
|
|
|
baebbc |
+ SAXSource source = xml.getSource(SAXSource.class);
|
|
|
baebbc |
+ sourceToString(source);
|
|
|
baebbc |
+
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+
|
|
|
baebbc |
+ @Test(expected = XMLStreamException.class)
|
|
|
baebbc |
+ public void testGetSourceXxeStAXSource() throws Exception {
|
|
|
baebbc |
+ PgSQLXML xml = new PgSQLXML(null, XXE_EXAMPLE);
|
|
|
baebbc |
+ StAXSource source = xml.getSource(StAXSource.class);
|
|
|
baebbc |
+ XMLStreamReader reader = source.getXMLStreamReader();
|
|
|
baebbc |
+ // STAX will not throw XXE error until we actually read the element
|
|
|
baebbc |
+ while (reader.hasNext()) {
|
|
|
baebbc |
+ reader.next();
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+ }
|
|
|
baebbc |
+}
|
|
|
baebbc |
diff --git a/pgjdbc/src/test/java/org/postgresql/test/jdbc2/Jdbc2TestSuite.java b/pgjdbc/src/test/java/org/postgresql/test/jdbc2/Jdbc2TestSuite.java
|
|
|
baebbc |
index 6314d21..814288c 100644
|
|
|
baebbc |
--- a/pgjdbc/src/test/java/org/postgresql/test/jdbc2/Jdbc2TestSuite.java
|
|
|
baebbc |
+++ b/pgjdbc/src/test/java/org/postgresql/test/jdbc2/Jdbc2TestSuite.java
|
|
|
baebbc |
@@ -13,6 +13,7 @@ import org.postgresql.core.ParserTest;
|
|
|
baebbc |
import org.postgresql.core.ReturningParserTest;
|
|
|
baebbc |
import org.postgresql.core.v3.V3ParameterListTests;
|
|
|
baebbc |
import org.postgresql.jdbc.DeepBatchedInsertStatementTest;
|
|
|
baebbc |
+import org.postgresql.jdbc.PgSQLXMLTest;
|
|
|
baebbc |
import org.postgresql.jdbc.PrimitiveArraySupportTest;
|
|
|
baebbc |
import org.postgresql.test.core.JavaVersionTest;
|
|
|
baebbc |
import org.postgresql.test.core.NativeQueryBindLengthTest;
|
|
|
baebbc |
@@ -76,6 +77,7 @@ import org.junit.runners.Suite;
|
|
|
baebbc |
TimestampTest.class,
|
|
|
baebbc |
TimezoneTest.class,
|
|
|
baebbc |
PGTimeTest.class,
|
|
|
baebbc |
+ PgSQLXMLTest.class,
|
|
|
baebbc |
PGTimestampTest.class,
|
|
|
baebbc |
TimezoneCachingTest.class,
|
|
|
baebbc |
ParserTest.class,
|
|
|
baebbc |
--
|
|
|
baebbc |
2.24.1
|
|
|
baebbc |
|