This is an automated email from the ASF dual-hosted git repository.

mawiesne pushed a commit to branch opennlp-2.x
in repository https://gitbox.apache.org/repos/asf/opennlp.git


The following commit(s) were added to refs/heads/opennlp-2.x by this push:
     new 3a37041ac [2.x] OPENNLP-1835: Tolerate unsupported XML parser security 
options (#1066)
3a37041ac is described below

commit 3a37041ace31a01e21172f89b1dd80e567f4e65e
Author: Artem Smirnov <[email protected]>
AuthorDate: Fri Jun 5 14:45:56 2026 +0300

    [2.x] OPENNLP-1835: Tolerate unsupported XML parser security options (#1066)
---
 .../src/main/java/opennlp/tools/util/XmlUtil.java  | 87 ++++++++++++++++----
 .../test/java/opennlp/tools/util/XmlUtilTest.java  | 92 ++++++++++++++++++++++
 2 files changed, 165 insertions(+), 14 deletions(-)

diff --git a/opennlp-tools/src/main/java/opennlp/tools/util/XmlUtil.java 
b/opennlp-tools/src/main/java/opennlp/tools/util/XmlUtil.java
index 96d2cfb7c..956f836e5 100644
--- a/opennlp-tools/src/main/java/opennlp/tools/util/XmlUtil.java
+++ b/opennlp-tools/src/main/java/opennlp/tools/util/XmlUtil.java
@@ -27,6 +27,8 @@ import javax.xml.parsers.SAXParserFactory;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.xml.sax.SAXException;
+import org.xml.sax.SAXNotRecognizedException;
+import org.xml.sax.SAXNotSupportedException;
 
 public class XmlUtil {
 
@@ -49,17 +51,17 @@ public class XmlUtil {
           " this platform.", e);
     }
     try {
-      documentBuilderFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, 
"");
-      documentBuilderFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, 
"");
-      documentBuilderFactory.setFeature(
+      setAttributeIfSupported(documentBuilderFactory, 
XMLConstants.ACCESS_EXTERNAL_DTD, "");
+      setAttributeIfSupported(documentBuilderFactory, 
XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
+      setFeatureIfSupported(documentBuilderFactory,
           "http://apache.org/xml/features/disallow-doctype-decl";, true);
-      documentBuilderFactory.setFeature(
+      setFeatureIfSupported(documentBuilderFactory,
           "http://xml.org/sax/features/external-general-entities";, false);
-      documentBuilderFactory.setFeature(
+      setFeatureIfSupported(documentBuilderFactory,
           "http://xml.org/sax/features/external-parameter-entities";, false);
-      documentBuilderFactory.setFeature(
+      setFeatureIfSupported(documentBuilderFactory,
           "http://apache.org/xml/features/nonvalidating/load-external-dtd";, 
false);
-      documentBuilderFactory.setXIncludeAware(false);
+      setXIncludeAwareIfSupported(documentBuilderFactory, false);
       documentBuilderFactory.setExpandEntityReferences(false);
       return documentBuilderFactory.newDocumentBuilder();
     } catch (ParserConfigurationException e) {
@@ -76,7 +78,7 @@ public class XmlUtil {
   public static SAXParser createSaxParser() {
     final SAXParserFactory spf = SAXParserFactory.newInstance();
     spf.setNamespaceAware(true);
-    spf.setXIncludeAware(false);
+    setXIncludeAwareIfSupported(spf, false);
     try {
       spf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
     } catch (ParserConfigurationException | SAXException e) {
@@ -85,17 +87,74 @@ public class XmlUtil {
       logger.warn("Failed to enable XMLConstants.FEATURE_SECURE_PROCESSING, 
it's unsupported on" +
           " this platform.", e);
     }
+    setFeatureIfSupported(spf, 
"http://apache.org/xml/features/disallow-doctype-decl";, true);
+    setFeatureIfSupported(spf, 
"http://xml.org/sax/features/external-general-entities";, false);
+    setFeatureIfSupported(spf, 
"http://xml.org/sax/features/external-parameter-entities";, false);
+    setFeatureIfSupported(spf, 
"http://apache.org/xml/features/nonvalidating/load-external-dtd";,
+        false);
     try {
-      spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl";, 
true);
-      spf.setFeature("http://xml.org/sax/features/external-general-entities";, 
false);
-      
spf.setFeature("http://xml.org/sax/features/external-parameter-entities";, 
false);
-      
spf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd";,
 false);
       final SAXParser parser = spf.newSAXParser();
-      parser.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
-      parser.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
+      setPropertyIfSupported(parser, XMLConstants.ACCESS_EXTERNAL_DTD, "");
+      setPropertyIfSupported(parser, XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
       return parser;
     } catch (ParserConfigurationException | SAXException e) {
       throw new IllegalStateException(e);
     }
   }
+
+  private static void setFeatureIfSupported(DocumentBuilderFactory factory, 
String name,
+                                            boolean value) {
+    try {
+      factory.setFeature(name, value);
+    } catch (ParserConfigurationException e) {
+      logger.warn("Failed to set XML parser feature {}, it's unsupported on 
this platform.",
+          name, e);
+    }
+  }
+
+  private static void setAttributeIfSupported(DocumentBuilderFactory factory, 
String name,
+                                             Object value) {
+    try {
+      factory.setAttribute(name, value);
+    } catch (IllegalArgumentException e) {
+      logger.warn("Failed to set XML parser attribute {}, it's unsupported on 
this platform.",
+          name, e);
+    }
+  }
+
+  private static void setXIncludeAwareIfSupported(DocumentBuilderFactory 
factory, boolean state) {
+    try {
+      factory.setXIncludeAware(state);
+    } catch (UnsupportedOperationException e) {
+      logger.warn("Failed to set XML parser XInclude awareness, it's 
unsupported on " +
+          "this platform.", e);
+    }
+  }
+
+  private static void setPropertyIfSupported(SAXParser parser, String name, 
Object value) {
+    try {
+      parser.setProperty(name, value);
+    } catch (SAXNotRecognizedException | SAXNotSupportedException e) {
+      logger.warn("Failed to set XML parser property {}, it's unsupported on 
this platform.",
+          name, e);
+    }
+  }
+
+  private static void setFeatureIfSupported(SAXParserFactory factory, String 
name, boolean value) {
+    try {
+      factory.setFeature(name, value);
+    } catch (ParserConfigurationException | SAXException e) {
+      logger.warn("Failed to set XML parser feature {}, it's unsupported on 
this platform.",
+          name, e);
+    }
+  }
+
+  private static void setXIncludeAwareIfSupported(SAXParserFactory factory, 
boolean state) {
+    try {
+      factory.setXIncludeAware(state);
+    } catch (UnsupportedOperationException e) {
+      logger.warn("Failed to set XML parser XInclude awareness, it's 
unsupported on " +
+          "this platform.", e);
+    }
+  }
 }
diff --git a/opennlp-tools/src/test/java/opennlp/tools/util/XmlUtilTest.java 
b/opennlp-tools/src/test/java/opennlp/tools/util/XmlUtilTest.java
new file mode 100644
index 000000000..e5cd82ae8
--- /dev/null
+++ b/opennlp-tools/src/test/java/opennlp/tools/util/XmlUtilTest.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package opennlp.tools.util;
+
+import java.io.StringReader;
+import javax.xml.XMLConstants;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.xml.sax.InputSource;
+
+public class XmlUtilTest {
+
+  @Test
+  void testCreateDocumentBuilderWithUnsupportedSecurityOptions() throws 
Exception {
+    String property = DocumentBuilderFactory.class.getName();
+    String oldFactory = System.getProperty(property);
+    System.setProperty(property, 
ThrowingSecurityOptionsDocumentBuilderFactory.class.getName());
+    try {
+      DocumentBuilder documentBuilder = XmlUtil.createDocumentBuilder();
+
+      Assertions.assertEquals("root", documentBuilder.parse(
+          new InputSource(new 
StringReader("<root/>"))).getDocumentElement().getTagName());
+    } finally {
+      if (oldFactory == null) {
+        System.clearProperty(property);
+      } else {
+        System.setProperty(property, oldFactory);
+      }
+    }
+  }
+
+  public static class ThrowingSecurityOptionsDocumentBuilderFactory
+      extends DocumentBuilderFactory {
+
+    private final DocumentBuilderFactory delegate = 
DocumentBuilderFactory.newDefaultInstance();
+
+    @Override
+    public DocumentBuilder newDocumentBuilder() throws 
ParserConfigurationException {
+      return delegate.newDocumentBuilder();
+    }
+
+    @Override
+    public void setAttribute(String name, Object value) {
+      if (XMLConstants.ACCESS_EXTERNAL_DTD.equals(name)) {
+        throw new IllegalArgumentException(name);
+      }
+      delegate.setAttribute(name, value);
+    }
+
+    @Override
+    public Object getAttribute(String name) {
+      return delegate.getAttribute(name);
+    }
+
+    @Override
+    public void setFeature(String name, boolean value) throws 
ParserConfigurationException {
+      if ("http://apache.org/xml/features/disallow-doctype-decl".equals(name)) 
{
+        throw new ParserConfigurationException(name);
+      }
+      delegate.setFeature(name, value);
+    }
+
+    @Override
+    public void setXIncludeAware(boolean state) {
+      throw new UnsupportedOperationException("XInclude");
+    }
+
+    @Override
+    public boolean getFeature(String name) throws ParserConfigurationException 
{
+      return delegate.getFeature(name);
+    }
+  }
+}

Reply via email to