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);
+ }
+ }
+}