|
16 | 16 | import java.io.Writer;
|
17 | 17 | import java.sql.SQLException;
|
18 | 18 | import java.sql.SQLXML;
|
| 19 | +import java.util.HashMap; |
| 20 | +import java.util.Map; |
19 | 21 |
|
| 22 | +import javax.xml.XMLConstants; |
| 23 | +import javax.xml.parsers.DocumentBuilder; |
20 | 24 | import javax.xml.parsers.DocumentBuilderFactory;
|
21 | 25 | import javax.xml.stream.XMLInputFactory;
|
22 | 26 | import javax.xml.stream.XMLOutputFactory;
|
23 | 27 | import javax.xml.transform.Result;
|
24 | 28 | import javax.xml.transform.Source;
|
25 | 29 | import javax.xml.transform.Transformer;
|
26 | 30 | import javax.xml.transform.TransformerFactory;
|
| 31 | +import javax.xml.transform.URIResolver; |
27 | 32 | import javax.xml.transform.dom.DOMResult;
|
28 | 33 | import javax.xml.transform.dom.DOMSource;
|
29 | 34 | import javax.xml.transform.sax.SAXResult;
|
|
39 | 44 | import org.h2.message.TraceObject;
|
40 | 45 | import org.h2.value.Value;
|
41 | 46 | import org.w3c.dom.Node;
|
| 47 | +import org.xml.sax.EntityResolver; |
42 | 48 | import org.xml.sax.InputSource;
|
| 49 | +import org.xml.sax.XMLReader; |
| 50 | +import org.xml.sax.helpers.DefaultHandler; |
| 51 | +import org.xml.sax.helpers.XMLReaderFactory; |
43 | 52 |
|
44 | 53 | /**
|
45 | 54 | * Represents a SQLXML value.
|
46 | 55 | */
|
47 | 56 | public final class JdbcSQLXML extends JdbcLob implements SQLXML {
|
48 | 57 |
|
| 58 | + private static final Map<String,Boolean> secureFeatureMap = new HashMap<>(); |
| 59 | + private static final EntityResolver NOOP_ENTITY_RESOLVER = (publicId, systemId) -> new InputSource(new StringReader("")); |
| 60 | + private static final URIResolver NOOP_URI_RESOLVER = (href, base) -> new StreamSource(new StringReader("")); |
| 61 | + |
| 62 | + static { |
| 63 | + secureFeatureMap.put(XMLConstants.FEATURE_SECURE_PROCESSING, true); |
| 64 | + secureFeatureMap.put("http://apache.org/xml/features/disallow-doctype-decl", true); |
| 65 | + secureFeatureMap.put("http://xml.org/sax/features/external-general-entities", false); |
| 66 | + secureFeatureMap.put("http://xml.org/sax/features/external-parameter-entities", false); |
| 67 | + secureFeatureMap.put("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); |
| 68 | + } |
| 69 | + |
49 | 70 | private DOMResult domResult;
|
50 | 71 |
|
51 | 72 | /**
|
@@ -107,15 +128,41 @@ public <T extends Source> T getSource(Class<T> sourceClass) throws SQLException
|
107 | 128 | "getSource(" + (sourceClass != null ? sourceClass.getSimpleName() + ".class" : "null") + ')');
|
108 | 129 | }
|
109 | 130 | checkReadable();
|
| 131 | + // According to https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html |
110 | 132 | if (sourceClass == null || sourceClass == DOMSource.class) {
|
111 | 133 | DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
112 |
| - return (T) new DOMSource(dbf.newDocumentBuilder().parse(new InputSource(value.getInputStream()))); |
| 134 | + for (Map.Entry<String,Boolean> entry : secureFeatureMap.entrySet()) { |
| 135 | + try { |
| 136 | + dbf.setFeature(entry.getKey(), entry.getValue()); |
| 137 | + } catch (Exception ignore) {/**/} |
| 138 | + } |
| 139 | + dbf.setXIncludeAware(false); |
| 140 | + dbf.setExpandEntityReferences(false); |
| 141 | + dbf.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); |
| 142 | + DocumentBuilder db = dbf.newDocumentBuilder(); |
| 143 | + db.setEntityResolver(NOOP_ENTITY_RESOLVER); |
| 144 | + return (T) new DOMSource(db.parse(new InputSource(value.getInputStream()))); |
113 | 145 | } else if (sourceClass == SAXSource.class) {
|
114 |
| - return (T) new SAXSource(new InputSource(value.getInputStream())); |
| 146 | + XMLReader reader = XMLReaderFactory.createXMLReader(); |
| 147 | + for (Map.Entry<String,Boolean> entry : secureFeatureMap.entrySet()) { |
| 148 | + try { |
| 149 | + reader.setFeature(entry.getKey(), entry.getValue()); |
| 150 | + } catch (Exception ignore) {/**/} |
| 151 | + } |
| 152 | + reader.setEntityResolver(NOOP_ENTITY_RESOLVER); |
| 153 | + return (T) new SAXSource(reader, new InputSource(value.getInputStream())); |
115 | 154 | } else if (sourceClass == StAXSource.class) {
|
116 | 155 | XMLInputFactory xif = XMLInputFactory.newInstance();
|
| 156 | + xif.setProperty(XMLInputFactory.SUPPORT_DTD, false); |
| 157 | + xif.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, ""); |
| 158 | + xif.setProperty("javax.xml.stream.isSupportingExternalEntities", false); |
117 | 159 | return (T) new StAXSource(xif.createXMLStreamReader(value.getInputStream()));
|
118 | 160 | } else if (sourceClass == StreamSource.class) {
|
| 161 | + TransformerFactory tf = TransformerFactory.newInstance(); |
| 162 | + tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); |
| 163 | + tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, ""); |
| 164 | + tf.setURIResolver(NOOP_URI_RESOLVER); |
| 165 | + tf.newTransformer().transform(new StreamSource(value.getInputStream()), new SAXResult(new DefaultHandler())); |
119 | 166 | return (T) new StreamSource(value.getInputStream());
|
120 | 167 | }
|
121 | 168 | throw unsupported(sourceClass.getName());
|
@@ -165,7 +212,7 @@ public <T extends Result> T setResult(Class<T> resultClass) throws SQLException
|
165 | 212 | try {
|
166 | 213 | if (isDebugEnabled()) {
|
167 | 214 | debugCode(
|
168 |
| - "getSource(" + (resultClass != null ? resultClass.getSimpleName() + ".class" : "null") + ')'); |
| 215 | + "setResult(" + (resultClass != null ? resultClass.getSimpleName() + ".class" : "null") + ')'); |
169 | 216 | }
|
170 | 217 | checkEditable();
|
171 | 218 | if (resultClass == null || resultClass == DOMResult.class) {
|
|
0 commit comments