namespaces) {
private Document createSecureDocument(InputSource in) throws ParserConfigurationException, IOException, SAXException
{
//https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html#xpathexpression
- DocumentBuilderFactory df = DocumentBuilderFactory.newInstance();
- df.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
- df.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
+ DocumentBuilderFactory df = XmlFactoryUtils.newDocumentBuilderFactory();
+ try {
+ df.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
+ df.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
+ } catch (IllegalArgumentException e) {
+ // JAXP 1.5 secure-processing attributes are not supported by outdated
+ // DocumentBuilderFactory implementations (e.g. Xerces 2.x found on the classpath):
+ // secure processing below still prevents external entity resolution, so ignore
+ }
df.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
DocumentBuilder builder = df.newDocumentBuilder();
return builder.parse(in);
diff --git a/src/main/java/org/cyclonedx/util/XmlFactoryUtils.java b/src/main/java/org/cyclonedx/util/XmlFactoryUtils.java
new file mode 100644
index 000000000..ad321b071
--- /dev/null
+++ b/src/main/java/org/cyclonedx/util/XmlFactoryUtils.java
@@ -0,0 +1,72 @@
+/*
+ * This file is part of CycloneDX Core (Java).
+ *
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) OWASP Foundation. All Rights Reserved.
+ */
+package org.cyclonedx.util;
+
+import javax.xml.XMLConstants;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.validation.SchemaFactory;
+
+/**
+ * Creates JAXP factories for XML processing, preferring the JDK's built-in system-default
+ * implementations over the JAXP lookup mechanism.
+ *
+ * The standard {@code newInstance()} lookup uses the classpath (ServiceLoader / system
+ * properties), so an outdated XML parser leaking onto the classpath (e.g. Xerces 2.x pulled in
+ * transitively by another library) would be picked up and break BOM parsing and validation with
+ * errors like {@code Property 'http://javax.xml.XMLConstants/property/accessExternalDTD' is not
+ * recognized}, because such parsers pre-date the JAXP 1.5 secure-processing properties.
+ * See cyclonedx-gradle-plugin#349.
+ *
+ * The {@code newDefaultInstance()} factory methods only exist since Java 9 while this library
+ * targets Java 8, so they are invoked reflectively, falling back to the standard lookup.
+ *
+ * @since 13.1.0
+ */
+public final class XmlFactoryUtils
+{
+ private XmlFactoryUtils() {
+ }
+
+ /**
+ * Creates a new {@link DocumentBuilderFactory}, preferring the JDK's built-in implementation.
+ *
+ * @return a new {@link DocumentBuilderFactory}
+ */
+ public static DocumentBuilderFactory newDocumentBuilderFactory() {
+ try {
+ return (DocumentBuilderFactory) DocumentBuilderFactory.class.getMethod("newDefaultInstance").invoke(null);
+ } catch (ReflectiveOperationException e) {
+ return DocumentBuilderFactory.newInstance();
+ }
+ }
+
+ /**
+ * Creates a new {@link SchemaFactory} for W3C XML Schema, preferring the JDK's built-in
+ * implementation.
+ *
+ * @return a new {@link SchemaFactory}
+ */
+ public static SchemaFactory newSchemaFactory() {
+ try {
+ return (SchemaFactory) SchemaFactory.class.getMethod("newDefaultInstance").invoke(null);
+ } catch (ReflectiveOperationException e) {
+ return SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+ }
+ }
+}