From 8f0207a8f2fa996e6f59ee93df79c6c8b6ebc200 Mon Sep 17 00:00:00 2001 From: JoeWang-Java Date: Thu, 30 Jul 2026 06:14:54 +0000 Subject: [PATCH] 8357394: Add JDK specific property for external resource access --- .../xalan/internal/xsltc/compiler/Import.java | 10 +- .../internal/xsltc/compiler/Include.java | 10 +- .../xalan/internal/xsltc/compiler/Parser.java | 35 +- .../xsltc/compiler/util/ErrorMessages.java | 6 +- .../internal/xsltc/dom/LoadDocument.java | 11 +- .../xsltc/runtime/AbstractTranslet.java | 40 +- .../internal/xsltc/trax/TemplatesImpl.java | 8 +- .../xsltc/trax/TransformerFactoryImpl.java | 10 +- .../internal/xsltc/trax/TransformerImpl.java | 26 +- .../impl/XMLDocumentFragmentScannerImpl.java | 3 +- .../internal/impl/XMLEntityManager.java | 17 +- .../internal/impl/msg/XMLMessages.properties | 5 +- .../impl/msg/XMLSchemaMessages.properties | 2 +- .../internal/impl/xs/XMLSchemaLoader.java | 21 +- .../impl/xs/traversers/XSDHandler.java | 7 +- .../internal/jaxp/DocumentBuilderImpl.java | 6 +- .../xerces/internal/jaxp/SAXParserImpl.java | 9 +- .../jaxp/validation/XMLSchemaFactory.java | 8 +- .../XMLSchemaValidatorComponentManager.java | 9 +- .../xerces/internal/parsers/DOMParser.java | 6 +- .../xerces/internal/parsers/SAXParser.java | 6 +- .../internal/xinclude/XIncludeHandler.java | 22 +- .../classes/jdk/xml/internal/AccessRule.java | 441 +++++++++++++++++ .../jdk/xml/internal/FeaturePropertyBase.java | 15 +- .../jdk/xml/internal/JdkConstants.java | 13 +- .../classes/jdk/xml/internal/JdkXmlUtils.java | 4 +- .../jdk/xml/internal/SecuritySupport.java | 115 +++-- .../share/classes/jdk/xml/internal/Utils.java | 52 +- .../jdk/xml/internal/XMLSecurityManager.java | 93 +++- .../internal/XMLSecurityPropertyManager.java | 12 +- src/java.xml/share/classes/module-info.java | 194 +++++++- src/java.xml/share/conf/jaxp.properties | 75 +++ .../common/access/AccessRuleTest.java | 147 ++++++ .../common/access/AccessTestBase.java | 447 ++++++++++++++++++ .../jaxp/unittest/common/access/DOMTest.java | 65 +++ .../jaxp/unittest/common/access/SAXTest.java | 67 +++ .../unittest/common/access/SchemaTest.java | 83 ++++ .../jaxp/unittest/common/access/StAXTest.java | 58 +++ .../unittest/common/access/TransformTest.java | 68 +++ .../unittest/common/access/XIncludeTest.java | 65 +++ .../catalog/testcatalog/TestCatalog.xml | 5 +- .../jaxp/unittest/common/dtd/DTDTestBase.java | 6 +- .../jaxp/unittest/common/util/TestBase.java | 113 ++++- .../unittest/common/xmlfiles/XI_simple.xml | 2 + 44 files changed, 2192 insertions(+), 225 deletions(-) create mode 100644 src/java.xml/share/classes/jdk/xml/internal/AccessRule.java create mode 100644 test/jaxp/javax/xml/jaxp/unittest/common/access/AccessRuleTest.java create mode 100644 test/jaxp/javax/xml/jaxp/unittest/common/access/AccessTestBase.java create mode 100644 test/jaxp/javax/xml/jaxp/unittest/common/access/DOMTest.java create mode 100644 test/jaxp/javax/xml/jaxp/unittest/common/access/SAXTest.java create mode 100644 test/jaxp/javax/xml/jaxp/unittest/common/access/SchemaTest.java create mode 100644 test/jaxp/javax/xml/jaxp/unittest/common/access/StAXTest.java create mode 100644 test/jaxp/javax/xml/jaxp/unittest/common/access/TransformTest.java create mode 100644 test/jaxp/javax/xml/jaxp/unittest/common/access/XIncludeTest.java create mode 100644 test/jaxp/javax/xml/jaxp/unittest/common/xmlfiles/XI_simple.xml diff --git a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/Import.java b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/Import.java index 098d3c8a260..3c8e1edd5b8 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/Import.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/Import.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -33,6 +33,7 @@ import java.util.Iterator; import javax.xml.XMLConstants; import jdk.xml.internal.JdkConstants; import jdk.xml.internal.SecuritySupport; +import jdk.xml.internal.XMLSecurityManager; import org.xml.sax.InputSource; import org.xml.sax.XMLReader; @@ -41,7 +42,7 @@ import org.xml.sax.XMLReader; * @author Morten Jorgensen * @author Erwin Bolwidt * @author Gunnlaugur Briem - * @LastModified: May 2021 + * @LastModified: July 2026 */ final class Import extends TopLevelElement { @@ -84,8 +85,9 @@ final class Import extends TopLevelElement { if (input == null) { docToLoad = SystemIDResolver.getAbsoluteURI(docToLoad, currLoadedDoc); String accessError = SecuritySupport.checkAccess(docToLoad, - (String)xsltc.getProperty(XMLConstants.ACCESS_EXTERNAL_STYLESHEET), - JdkConstants.ACCESS_EXTERNAL_ALL); + (XMLSecurityManager)xsltc.getProperty(JdkConstants.SECURITY_MANAGER), + XMLConstants.ACCESS_EXTERNAL_STYLESHEET, + (String)xsltc.getProperty(XMLConstants.ACCESS_EXTERNAL_STYLESHEET)); if (accessError != null) { final ErrorMsg msg = new ErrorMsg(ErrorMsg.ACCESSING_XSLT_TARGET_ERR, diff --git a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/Include.java b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/Include.java index ecb03df9f1c..8e3b5530320 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/Include.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/Include.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -33,6 +33,7 @@ import java.util.Iterator; import javax.xml.XMLConstants; import jdk.xml.internal.JdkConstants; import jdk.xml.internal.SecuritySupport; +import jdk.xml.internal.XMLSecurityManager; import org.xml.sax.InputSource; import org.xml.sax.XMLReader; @@ -41,7 +42,7 @@ import org.xml.sax.XMLReader; * @author Morten Jorgensen * @author Erwin Bolwidt * @author Gunnlaugur Briem - * @LastModified: May 2021 + * @LastModified: July 2026 */ final class Include extends TopLevelElement { @@ -84,8 +85,9 @@ final class Include extends TopLevelElement { if (input == null) { docToLoad = SystemIDResolver.getAbsoluteURI(docToLoad, currLoadedDoc); String accessError = SecuritySupport.checkAccess(docToLoad, - (String)xsltc.getProperty(XMLConstants.ACCESS_EXTERNAL_STYLESHEET), - JdkConstants.ACCESS_EXTERNAL_ALL); + (XMLSecurityManager)xsltc.getProperty(JdkConstants.SECURITY_MANAGER), + XMLConstants.ACCESS_EXTERNAL_STYLESHEET, + (String)xsltc.getProperty(XMLConstants.ACCESS_EXTERNAL_STYLESHEET)); if (accessError != null) { final ErrorMsg msg = new ErrorMsg(ErrorMsg.ACCESSING_XSLT_TARGET_ERR, diff --git a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/Parser.java b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/Parser.java index 0dde14f455c..f523b65a2b4 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/Parser.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/Parser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -60,7 +60,7 @@ import org.xml.sax.helpers.AttributesImpl; * @author G. Todd Miller * @author Morten Jorgensen * @author Erwin Bolwidt - * @LastModified: July 2023 + * @LastModified: July 2026 */ public class Parser implements Constants, ContentHandler { @@ -540,23 +540,20 @@ public class Parser implements Constants, ContentHandler { return(element); } else { - try { - String path = _target; - if (path.indexOf(":")==-1) { - path = "file:" + path; - } - path = SystemIDResolver.getAbsoluteURI(path); - String accessError = SecuritySupport.checkAccess(path, - (String)_xsltc.getProperty(XMLConstants.ACCESS_EXTERNAL_STYLESHEET), - JdkConstants.ACCESS_EXTERNAL_ALL); - if (accessError != null) { - ErrorMsg msg = new ErrorMsg(ErrorMsg.ACCESSING_XSLT_TARGET_ERR, - SecuritySupport.sanitizePath(_target), accessError, - root); - throw new CompilerException(msg.toString()); - } - } catch (IOException ex) { - throw new CompilerException(ex); + String path = _target; + if (path.indexOf(":")==-1) { + path = "file:" + path; + } + path = SystemIDResolver.getAbsoluteURI(path); + String accessError = SecuritySupport.checkAccess(path, + (XMLSecurityManager)_xsltc.getProperty(JdkConstants.SECURITY_MANAGER), + XMLConstants.ACCESS_EXTERNAL_STYLESHEET, + (String)_xsltc.getProperty(XMLConstants.ACCESS_EXTERNAL_STYLESHEET)); + if (accessError != null) { + ErrorMsg msg = new ErrorMsg(ErrorMsg.ACCESSING_XSLT_TARGET_ERR, + SecuritySupport.sanitizePath(_target), accessError, + root); + throw new CompilerException(msg.toString()); } return(loadExternalStylesheet(_target)); diff --git a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/util/ErrorMessages.java b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/util/ErrorMessages.java index 660d1194b30..a3875969bb7 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/util/ErrorMessages.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/util/ErrorMessages.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -24,7 +24,7 @@ import java.util.ListResourceBundle; /** * @author Morten Jorgensen - * @LastModified: Dec 2024 + * @LastModified: July 2026 */ public class ErrorMessages extends ListResourceBundle { @@ -469,7 +469,7 @@ public class ErrorMessages extends ListResourceBundle { * Note to translators: access to the stylesheet target is denied */ {ErrorMsg.ACCESSING_XSLT_TARGET_ERR, - "Could not read stylesheet target ''{0}'', because ''{1}'' access is not allowed due to restriction set by the accessExternalStylesheet property."}, + "Could not read stylesheet target ''{0}'', because access is not allowed due to restriction set by ''{1}''."}, /* * Note to translators: This message represents an internal error in diff --git a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/dom/LoadDocument.java b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/dom/LoadDocument.java index 39084c87f0b..e17407e2831 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/dom/LoadDocument.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/dom/LoadDocument.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -39,7 +39,7 @@ import jdk.xml.internal.SecuritySupport; /** * @author Morten Jorgensen - * @LastModified: Sept 2021 + * @LastModified: July 2026 */ public final class LoadDocument { @@ -197,12 +197,7 @@ public final class LoadDocument { throw new TransletException(e); } } else { - String accessError = SecuritySupport.checkAccess(uri, translet.getAllowedProtocols(), JdkConstants.ACCESS_EXTERNAL_ALL); - if (accessError != null) { - ErrorMsg msg = new ErrorMsg(ErrorMsg.ACCESSING_XSLT_TARGET_ERR, - SecuritySupport.sanitizePath(uri), accessError); - throw new Exception(msg.toString()); - } + translet.verifyAccess(uri); // Parse the input document and construct DOM object // Trust the DTMManager to pick the right parser and diff --git a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/runtime/AbstractTranslet.java b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/runtime/AbstractTranslet.java index 1612fd61923..620eb670547 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/runtime/AbstractTranslet.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/runtime/AbstractTranslet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2026, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -25,6 +25,7 @@ import com.sun.org.apache.xalan.internal.xsltc.DOMCache; import com.sun.org.apache.xalan.internal.xsltc.DOMEnhancedForDTM; import com.sun.org.apache.xalan.internal.xsltc.Translet; import com.sun.org.apache.xalan.internal.xsltc.TransletException; +import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg; import com.sun.org.apache.xalan.internal.xsltc.dom.DOMAdapter; import com.sun.org.apache.xalan.internal.xsltc.dom.KeyIndex; import com.sun.org.apache.xalan.internal.xsltc.runtime.output.TransletOutputHandlerFactory; @@ -40,11 +41,14 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import javax.xml.XMLConstants; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Templates; import jdk.xml.internal.JdkConstants; import jdk.xml.internal.JdkXmlUtils; +import jdk.xml.internal.SecuritySupport; +import jdk.xml.internal.XMLSecurityManager; import org.w3c.dom.DOMImplementation; import org.w3c.dom.Document; @@ -54,7 +58,7 @@ import org.w3c.dom.Document; * @author Morten Jorgensen * @author G. Todd Miller * @author John Howard, JohnH@schemasoft.com - * @LastModified: Sept 2021 + * @LastModified: July 2026 */ public abstract class AbstractTranslet implements Translet { @@ -115,6 +119,7 @@ public abstract class AbstractTranslet implements Translet { * protocols allowed for external references set by the stylesheet processing instruction, Document() function, Import and Include element. */ private String _accessExternalStylesheet = JdkConstants.EXTERNAL_ACCESS_DEFAULT; + private XMLSecurityManager _xsm; // The error message when access to exteranl resources is rejected private String _accessErr = null; @@ -789,6 +794,37 @@ public abstract class AbstractTranslet implements Translet { _accessExternalStylesheet = protocols; } + /** + * Returns the XMLSecurityManager + */ + public XMLSecurityManager getXMLSecurityManager() { + return _xsm; + } + + /** + * Sets the XMLSecurityManager + * @param xsm the XMLSecurityManager instance + */ + public void setXMLSecurityManager(XMLSecurityManager xsm) { + _xsm = xsm; + } + + /** + * Verifies that access to the resource represented by the systemId is permitted. + * @param systemId the systemId + + * @throws TransletException if access is not permitted + */ + public void verifyAccess(String systemId) throws TransletException { + String accessError = SecuritySupport.checkAccess(systemId, _xsm, + XMLConstants.ACCESS_EXTERNAL_STYLESHEET, _accessExternalStylesheet); + if (accessError != null) { + ErrorMsg msg = new ErrorMsg(ErrorMsg.ACCESSING_XSLT_TARGET_ERR, + SecuritySupport.sanitizePath(systemId), accessError); + throw new TransletException(msg.toString()); + } + } + /** * Returns the access error. */ diff --git a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TemplatesImpl.java b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TemplatesImpl.java index 41b83bce5df..71aad15b09f 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TemplatesImpl.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TemplatesImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -58,6 +58,7 @@ import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.URIResolver; import jdk.xml.internal.JdkConstants; +import jdk.xml.internal.XMLSecurityManager; /** @@ -65,7 +66,7 @@ import jdk.xml.internal.JdkConstants; * @author G. Todd Millerj * @author Jochen Cordes * @author Santiago Pericas-Geertsen - * @LastModified: Jan 2025 + * @LastModified: July 2026 */ public final class TemplatesImpl implements Templates, Serializable { static final long serialVersionUID = 673094361519270707L; @@ -147,6 +148,7 @@ public final class TemplatesImpl implements Templates, Serializable { * protocols allowed for external references set by the stylesheet processing instruction, Import and Include element. */ private transient String _accessExternalStylesheet = JdkConstants.EXTERNAL_ACCESS_DEFAULT; + private transient XMLSecurityManager _xsm; /** * @serialField _name String The Name of the main class @@ -240,6 +242,7 @@ public final class TemplatesImpl implements Templates, Serializable { _tfactory = tfactory; _overrideDefaultParser = tfactory.overrideDefaultParser(); _accessExternalStylesheet = (String) tfactory.getAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET); + _xsm = (XMLSecurityManager) tfactory.getAttribute(JdkConstants.SECURITY_MANAGER); } /** * Need for de-serialization, see readObject(). @@ -540,6 +543,7 @@ public final class TemplatesImpl implements Templates, Serializable { translet.setTemplates(this); translet.setOverrideDefaultParser(_overrideDefaultParser); translet.setAllowedProtocols(_accessExternalStylesheet); + translet.setXMLSecurityManager(_xsm); if (_auxClasses != null) { translet.setAuxiliaryClasses(_auxClasses); } diff --git a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerFactoryImpl.java b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerFactoryImpl.java index ca56223aa44..14bb3d0df82 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerFactoryImpl.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerFactoryImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -88,7 +88,7 @@ import org.xml.sax.XMLReader; * @author G. Todd Miller * @author Morten Jorgensen * @author Santiago Pericas-Geertsen - * @LastModified: May 2025 + * @LastModified: July 2026 */ public class TransformerFactoryImpl extends SAXTransformerFactory implements SourceLoader @@ -515,7 +515,7 @@ public class TransformerFactoryImpl } if (_xmlSecurityPropertyMgr != null && - _xmlSecurityPropertyMgr.setValue(name, FeaturePropertyBase.State.APIPROPERTY, value)) { + _xmlSecurityPropertyMgr.setValue(name, JdkProperty.State.APIPROPERTY, value)) { _accessExternalDTD = _xmlSecurityPropertyMgr.getValue( Property.ACCESS_EXTERNAL_DTD); _accessExternalStylesheet = _xmlSecurityPropertyMgr.getValue( @@ -572,9 +572,9 @@ public class TransformerFactoryImpl // set external access restriction when FSP is explicitly set if (value) { _xmlSecurityPropertyMgr.setValue(Property.ACCESS_EXTERNAL_DTD, - FeaturePropertyBase.State.FSP, JdkConstants.EXTERNAL_ACCESS_DEFAULT_FSP); + JdkProperty.State.FSP, JdkConstants.EXTERNAL_ACCESS_DEFAULT_FSP); _xmlSecurityPropertyMgr.setValue(Property.ACCESS_EXTERNAL_STYLESHEET, - FeaturePropertyBase.State.FSP, JdkConstants.EXTERNAL_ACCESS_DEFAULT_FSP); + JdkProperty.State.FSP, JdkConstants.EXTERNAL_ACCESS_DEFAULT_FSP); _accessExternalDTD = _xmlSecurityPropertyMgr.getValue( Property.ACCESS_EXTERNAL_DTD); _accessExternalStylesheet = _xmlSecurityPropertyMgr.getValue( diff --git a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerImpl.java b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerImpl.java index b48b9d2a657..89091355484 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerImpl.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -100,7 +100,7 @@ import org.xml.sax.ext.LexicalHandler; * @author Morten Jorgensen * @author G. Todd Miller * @author Santiago Pericas-Geertsen - * @LastModified: Jan 2025 + * @LastModified: July 2026 */ public final class TransformerImpl extends Transformer implements DOMCache @@ -1357,21 +1357,13 @@ public final class TransformerImpl extends Transformer */ AbstractTranslet t = (AbstractTranslet)translet; String systemId = SystemIDResolver.getAbsoluteURI(href, baseURI); - String errMsg = null; - try { - String accessError = SecuritySupport.checkAccess(systemId, - t.getAllowedProtocols(), - JdkConstants.ACCESS_EXTERNAL_ALL); - if (accessError != null) { - ErrorMsg msg = new ErrorMsg(ErrorMsg.ACCESSING_XSLT_TARGET_ERR, - SecuritySupport.sanitizePath(href), accessError); - errMsg = msg.toString(); - } - } catch (IOException ioe) { - errMsg = ioe.getMessage(); - } - if (errMsg != null) { - t.setAccessError(errMsg); + String accessError = SecuritySupport.checkAccess(systemId, + t.getXMLSecurityManager(), XMLConstants.ACCESS_EXTERNAL_STYLESHEET, + t.getAllowedProtocols()); + if (accessError != null) { + ErrorMsg msg = new ErrorMsg(ErrorMsg.ACCESSING_XSLT_TARGET_ERR, + SecuritySupport.sanitizePath(href), accessError); + t.setAccessError(msg.toString()); return null; } diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLDocumentFragmentScannerImpl.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLDocumentFragmentScannerImpl.java index ccaa9f6c5dd..e9cfd0ff388 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLDocumentFragmentScannerImpl.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLDocumentFragmentScannerImpl.java @@ -2023,7 +2023,8 @@ public class XMLDocumentFragmentScannerImpl String checkAccess(String systemId, String allowedProtocols) throws IOException { String baseSystemId = fEntityScanner.getBaseSystemId(); String expandedSystemId = XMLEntityManager.expandSystemId(systemId, baseSystemId, fStrictURI); - return SecuritySupport.checkAccess(expandedSystemId, allowedProtocols, JdkConstants.ACCESS_EXTERNAL_ALL); + return SecuritySupport.checkAccess(expandedSystemId, fSecurityManager, + XMLConstants.ACCESS_EXTERNAL_DTD, allowedProtocols); } // diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLEntityManager.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLEntityManager.java index 99b4193b39c..75f7d5216c7 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLEntityManager.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLEntityManager.java @@ -1361,20 +1361,15 @@ public class XMLEntityManager implements XMLComponent, XMLEntityResolver { if (external) { staxInputSource = resolveEntityAsPerStax(externalEntity.entityLocation); - /** xxx: Waiting from the EG - * //simply return if there was entity resolver registered and application - * //returns either XMLStreamReader or XMLEventReader. - * if(staxInputSource.hasXMLStreamOrXMLEventReader()) return ; - */ xmlInputSource = staxInputSource.getXMLInputSource() ; if (!fISCreatedByResolver) { - String accessError = SecuritySupport.checkAccess(expandedSystemId, - fAccessExternalDTD, JdkConstants.ACCESS_EXTERNAL_ALL); + String accessError = SecuritySupport.checkAccess(expandedSystemId, fSecurityManager, + XMLConstants.ACCESS_EXTERNAL_DTD, fAccessExternalDTD); if (accessError != null) { - fErrorReporter.reportError(this.getEntityScanner(),XMLMessageFormatter.XML_DOMAIN, - "AccessExternalEntity", - new Object[] { SecuritySupport.sanitizePath(expandedSystemId), accessError }, - XMLErrorReporter.SEVERITY_FATAL_ERROR); + fErrorReporter.reportError(this.getEntityScanner(), XMLMessageFormatter.XML_DOMAIN, + "AccessExternalEntity", + new Object[]{SecuritySupport.sanitizePath(expandedSystemId), accessError}, + XMLErrorReporter.SEVERITY_FATAL_ERROR); } } } diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/msg/XMLMessages.properties b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/msg/XMLMessages.properties index 25182a06a3c..b51e912370a 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/msg/XMLMessages.properties +++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/msg/XMLMessages.properties @@ -283,8 +283,9 @@ # Entity related messages # 3.1 Start-Tags, End-Tags, and Empty-Element Tags ReferenceToExternalEntity = The external entity reference \"&{0};\" is not permitted in an attribute value. - AccessExternalDTD = External DTD: Failed to read external DTD ''{0}'', because ''{1}'' access is not allowed due to restriction set by the accessExternalDTD property. - AccessExternalEntity = External Entity: Failed to read external document ''{0}'', because ''{1}'' access is not allowed due to restriction set by the accessExternalDTD property. + AccessExternalDTD = External DTD: Failed to read external DTD ''{0}'', because access is not allowed due to restriction set by ''{1}''. + AccessExternalEntity = External Entity: Failed to read external document ''{0}'', because access is not allowed due to restriction set by ''{1}''. + ResourceAccess = Resource Access: Failed to read external document ''{0}'', because the ''{1}'' property is set to not allow accessing the document. # 4.1 Character and Entity References EntityNotDeclared = The entity \"{0}\" was referenced, but not declared. diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/msg/XMLSchemaMessages.properties b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/msg/XMLSchemaMessages.properties index 4f15b428c8b..2004e41e51d 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/msg/XMLSchemaMessages.properties +++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/msg/XMLSchemaMessages.properties @@ -108,7 +108,7 @@ #schema valid (3.X.3) - schema_reference.access = schema_reference: Failed to read schema document ''{0}'', because ''{1}'' access is not allowed due to restriction set by the accessExternalSchema property. + schema_reference.access = schema_reference: Failed to read schema document ''{0}'', because access is not allowed due to restriction set by ''{1}''. schema_reference.4 = schema_reference.4: Failed to read schema document ''{0}'', because 1) could not find the document; 2) the document could not be read; 3) the root element of the document is not . src-annotation = src-annotation: elements can only contain and elements, but ''{0}'' was found. src-attribute.1 = src-attribute.1: The properties ''default'' and ''fixed'' cannot both be present in attribute declaration ''{0}''. Use only one of them. diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xs/XMLSchemaLoader.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xs/XMLSchemaLoader.java index b4b76546d6d..c884eec6f9f 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xs/XMLSchemaLoader.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xs/XMLSchemaLoader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2026, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -103,7 +103,7 @@ import org.xml.sax.InputSource; * @xerces.internal * * @author Neil Graham, IBM - * @LastModified: May 2025 + * @LastModified: July 2026 */ public class XMLSchemaLoader implements XMLGrammarLoader, XMLComponent, XSElementDeclHelper, @@ -285,6 +285,7 @@ XSLoader, DOMConfiguration { private CMBuilder fCMBuilder; private XSDDescription fXSDDescription = new XSDDescription(); private String faccessExternalSchema = JdkConstants.EXTERNAL_ACCESS_DEFAULT; + private XMLSecurityManager fXMLScurityManager = null; private WeakHashMap fJAXPCache; private Locale fLocale = Locale.getDefault(); @@ -476,6 +477,9 @@ XSLoader, DOMConfiguration { XMLSecurityPropertyManager spm = (XMLSecurityPropertyManager)state; faccessExternalSchema = spm.getValue(XMLSecurityPropertyManager.Property.ACCESS_EXTERNAL_SCHEMA); } + else if (propertyId.equals(SECURITY_MANAGER)) { + fXMLScurityManager = (XMLSecurityManager)state; + } } // setProperty(String, Object) /** @@ -567,6 +571,7 @@ XSLoader, DOMConfiguration { desc.fContextType = XSDDescription.CONTEXT_PREPARSE; desc.setBaseSystemId(source.getBaseSystemId()); desc.setLiteralSystemId( source.getSystemId()); + fXSDDescription = desc; // none of the other fields make sense for preparsing Map locationPairs = new HashMap<>(); // Process external schema location properties. @@ -607,8 +612,9 @@ XSLoader, DOMConfiguration { processJAXPSchemaSource(locationPairs); } - if (desc.isExternal() && !source.isCreatedByResolver()) { - String accessError = SecuritySupport.checkAccess(desc.getExpandedSystemId(), faccessExternalSchema, JdkConstants.ACCESS_EXTERNAL_ALL); + if (fXSDDescription.isExternal() && !source.isCreatedByResolver()) { + String accessError = SecuritySupport.checkAccess(desc.getExpandedSystemId(), + fXMLScurityManager, XMLConstants.ACCESS_EXTERNAL_SCHEMA, faccessExternalSchema); if (accessError != null) { throw new XNIException(fErrorReporter.reportError(XSMessageFormatter.SCHEMA_DOMAIN, "schema_reference.access", @@ -1006,9 +1012,10 @@ XSLoader, DOMConfiguration { setProperty(XML_SECURITY_PROPERTY_MANAGER, spm); } - XMLSecurityManager sm = (XMLSecurityManager)componentManager.getProperty(SECURITY_MANAGER); - if (sm == null) - setProperty(SECURITY_MANAGER, JdkXmlConfig.getInstance(false).getXMLSecurityManager(false)); + fXMLScurityManager = (XMLSecurityManager)componentManager.getProperty(SECURITY_MANAGER); + if (fXMLScurityManager == null) + fXMLScurityManager = JdkXmlConfig.getInstance(false).getXMLSecurityManager(false); + setProperty(SECURITY_MANAGER, fXMLScurityManager); faccessExternalSchema = spm.getValue(XMLSecurityPropertyManager.Property.ACCESS_EXTERNAL_SCHEMA); diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xs/traversers/XSDHandler.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xs/traversers/XSDHandler.java index 13973615adb..50660b9fbcf 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xs/traversers/XSDHandler.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xs/traversers/XSDHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -132,7 +132,7 @@ import org.xml.sax.XMLReader; * @author Neil Graham, IBM * @author Pavani Mukthipudi, Sun Microsystems * - * @LastModified: May 2025 + * @LastModified: July 2026 */ public class XSDHandler { @@ -2216,7 +2216,8 @@ public class XSDHandler { if ((!schemaSource.isCreatedByResolver()) && (referType == XSDDescription.CONTEXT_IMPORT || referType == XSDDescription.CONTEXT_INCLUDE || referType == XSDDescription.CONTEXT_REDEFINE)) { - String accessError = SecuritySupport.checkAccess(schemaId, fAccessExternalSchema, JdkConstants.ACCESS_EXTERNAL_ALL); + String accessError = SecuritySupport.checkAccess(schemaId, + fSecurityManager, XMLConstants.ACCESS_EXTERNAL_SCHEMA, fAccessExternalSchema); if (accessError != null) { reportSchemaFatalError("schema_reference.access", new Object[] { SecuritySupport.sanitizePath(schemaId), accessError }, diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/DocumentBuilderImpl.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/DocumentBuilderImpl.java index c885cb58cbc..8f96e955880 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/DocumentBuilderImpl.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/DocumentBuilderImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -41,7 +41,7 @@ import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager; import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException; import com.sun.org.apache.xerces.internal.xni.parser.XMLDocumentSource; import com.sun.org.apache.xerces.internal.xni.parser.XMLParserConfiguration; -import jdk.xml.internal.FeaturePropertyBase.State; +import jdk.xml.internal.JdkProperty.State; import jdk.xml.internal.JdkConstants; import jdk.xml.internal.JdkXmlUtils; import jdk.xml.internal.XMLSecurityManager; @@ -59,7 +59,7 @@ import org.xml.sax.SAXNotSupportedException; /** * @author Rajiv Mordani * @author Edwin Goei - * @LastModified: May 2025 + * @LastModified: July 2026 */ public class DocumentBuilderImpl extends DocumentBuilder implements JAXPConstants diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/SAXParserImpl.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/SAXParserImpl.java index 9e729c9a9a2..cc61b41648b 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/SAXParserImpl.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/SAXParserImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2026, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -43,6 +43,7 @@ import javax.xml.XMLConstants; import javax.xml.validation.Schema; import jdk.xml.internal.FeaturePropertyBase; import jdk.xml.internal.JdkConstants; +import jdk.xml.internal.JdkProperty; import jdk.xml.internal.JdkXmlConfig; import jdk.xml.internal.JdkXmlUtils; import jdk.xml.internal.XMLSecurityManager; @@ -65,7 +66,7 @@ import org.xml.sax.helpers.DefaultHandler; * @author Rajiv Mordani * @author Edwin Goei * - * @LastModified: May 2025 + * @LastModified: July 2026 */ @SuppressWarnings("deprecation") public class SAXParserImpl extends javax.xml.parsers.SAXParser @@ -171,9 +172,9 @@ public class SAXParserImpl extends javax.xml.parsers.SAXParser Boolean temp = features.get(XMLConstants.FEATURE_SECURE_PROCESSING); if (temp != null && temp) { fSecurityPropertyMgr.setValue(XMLSecurityPropertyManager.Property.ACCESS_EXTERNAL_DTD, - FeaturePropertyBase.State.FSP, JdkConstants.EXTERNAL_ACCESS_DEFAULT_FSP); + JdkProperty.State.FSP, JdkConstants.EXTERNAL_ACCESS_DEFAULT_FSP); fSecurityPropertyMgr.setValue(XMLSecurityPropertyManager.Property.ACCESS_EXTERNAL_SCHEMA, - FeaturePropertyBase.State.FSP, JdkConstants.EXTERNAL_ACCESS_DEFAULT_FSP); + JdkProperty.State.FSP, JdkConstants.EXTERNAL_ACCESS_DEFAULT_FSP); } } } diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/validation/XMLSchemaFactory.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/validation/XMLSchemaFactory.java index fff15165ae8..58ff7f52e45 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/validation/XMLSchemaFactory.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/validation/XMLSchemaFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -71,7 +71,7 @@ import org.xml.sax.SAXParseException; * * @author Kohsuke Kawaguchi * - * @LastModified: May 2025 + * @LastModified: July 2026 */ public final class XMLSchemaFactory extends SchemaFactory { @@ -446,9 +446,9 @@ public final class XMLSchemaFactory extends SchemaFactory { fSecurityManager.setSecureProcessing(value); if (value) { fSecurityPropertyMgr.setValue(XMLSecurityPropertyManager.Property.ACCESS_EXTERNAL_DTD, - FeaturePropertyBase.State.FSP, JdkConstants.EXTERNAL_ACCESS_DEFAULT_FSP); + JdkProperty.State.FSP, JdkConstants.EXTERNAL_ACCESS_DEFAULT_FSP); fSecurityPropertyMgr.setValue(XMLSecurityPropertyManager.Property.ACCESS_EXTERNAL_SCHEMA, - FeaturePropertyBase.State.FSP, JdkConstants.EXTERNAL_ACCESS_DEFAULT_FSP); + JdkProperty.State.FSP, JdkConstants.EXTERNAL_ACCESS_DEFAULT_FSP); } fXMLSchemaLoader.setProperty(SECURITY_MANAGER, fSecurityManager); diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/validation/XMLSchemaValidatorComponentManager.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/validation/XMLSchemaValidatorComponentManager.java index b53c582e41f..5939c96affa 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/validation/XMLSchemaValidatorComponentManager.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/validation/XMLSchemaValidatorComponentManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2026, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -48,6 +48,7 @@ import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException; import javax.xml.catalog.CatalogFeatures; import jdk.xml.internal.FeaturePropertyBase; import jdk.xml.internal.JdkConstants; +import jdk.xml.internal.JdkProperty; import jdk.xml.internal.JdkXmlConfig; import jdk.xml.internal.JdkXmlUtils; import jdk.xml.internal.XMLSecurityManager; @@ -59,7 +60,7 @@ import org.xml.sax.ErrorHandler; *

An implementation of XMLComponentManager for a schema validator.

* * @author Michael Glavassevich, IBM - * @LastModified: May 2025 + * @LastModified: July 2026 */ final class XMLSchemaValidatorComponentManager extends ParserConfigurationSettings implements XMLComponentManager { @@ -378,9 +379,9 @@ final class XMLSchemaValidatorComponentManager extends ParserConfigurationSettin if (value) { fSecurityPropertyMgr.setValue(XMLSecurityPropertyManager.Property.ACCESS_EXTERNAL_DTD, - FeaturePropertyBase.State.FSP, JdkConstants.EXTERNAL_ACCESS_DEFAULT_FSP); + JdkProperty.State.FSP, JdkConstants.EXTERNAL_ACCESS_DEFAULT_FSP); fSecurityPropertyMgr.setValue(XMLSecurityPropertyManager.Property.ACCESS_EXTERNAL_SCHEMA, - FeaturePropertyBase.State.FSP, JdkConstants.EXTERNAL_ACCESS_DEFAULT_FSP); + JdkProperty.State.FSP, JdkConstants.EXTERNAL_ACCESS_DEFAULT_FSP); setProperty(XML_SECURITY_PROPERTY_MANAGER, fSecurityPropertyMgr); } diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/DOMParser.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/DOMParser.java index 9bc3d319619..8597af5f76e 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/DOMParser.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/DOMParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -61,7 +61,7 @@ import org.xml.sax.helpers.LocatorImpl; * * @author Arnaud Le Hors, IBM * @author Andy Clark, IBM - * @LastModified: May 2025 + * @LastModified: July 2026 */ public class DOMParser extends AbstractDOMParser { @@ -575,7 +575,7 @@ public class DOMParser * internally the support of this property is done through * XMLSecurityPropertyManager */ - securityPropertyManager.setValue(index, FeaturePropertyBase.State.APIPROPERTY, (String)value); + securityPropertyManager.setValue(index, JdkProperty.State.APIPROPERTY, (String)value); } else { //check if the property is managed by security manager if (!securityManager.setLimit(propertyId, JdkProperty.State.APIPROPERTY, value)) { diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/SAXParser.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/SAXParser.java index 52be34a5a37..922177bb740 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/SAXParser.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/SAXParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -42,7 +42,7 @@ import org.xml.sax.SAXNotSupportedException; * @author Arnaud Le Hors, IBM * @author Andy Clark, IBM * - * @LastModified: May 2025 + * @LastModified: July 2026 */ public class SAXParser extends AbstractSAXParser { @@ -169,7 +169,7 @@ public class SAXParser * internally the support of this property is done through * XMLSecurityPropertyManager */ - securityPropertyManager.setValue(index, FeaturePropertyBase.State.APIPROPERTY, (String)value); + securityPropertyManager.setValue(index, JdkProperty.State.APIPROPERTY, (String)value); } else { //check if the property is managed by security manager if (!securityManager.setLimit(name, JdkProperty.State.APIPROPERTY, value)) { diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/xinclude/XIncludeHandler.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/xinclude/XIncludeHandler.java index 833790834ac..67b7eaf471c 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/xinclude/XIncludeHandler.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/xinclude/XIncludeHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2026, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -78,6 +78,7 @@ import javax.xml.catalog.CatalogResolver; import javax.xml.transform.Source; import jdk.xml.internal.JdkConstants; import jdk.xml.internal.JdkXmlUtils; +import jdk.xml.internal.SecuritySupport; import jdk.xml.internal.XMLSecurityManager; import jdk.xml.internal.XMLSecurityPropertyManager; import org.xml.sax.InputSource; @@ -128,7 +129,7 @@ import org.xml.sax.InputSource; * * * @see XIncludeNamespaceSupport - * @LastModified: Apr 2025 + * @LastModified: July 2026 */ public class XIncludeHandler implements XMLComponent, XMLDocumentFilter, XMLDTDFilter { @@ -1704,6 +1705,23 @@ public class XIncludeHandler } if (includedSource == null) { + String expandedSystemId; + try { + expandedSystemId = XMLEntityManager.expandSystemId( + href, fCurrentBaseURI.getExpandedSystemId(), false); + } catch (MalformedURIException e) { + reportResourceError("XMLResourceError", + new Object[] { href, e.getMessage()}, e); + return false; + } + String accessError = SecuritySupport.checkAccess(expandedSystemId, + fSecurityManager, null, JdkConstants.ACCESS_EXTERNAL_ALL); + if (accessError != null) { + reportFatalError("XMLResourceError", + new Object[] { href, accessError}); + return false; + } + // setup an HTTPInputSource if either of the content negotation attributes were specified. if (accept != null || acceptLanguage != null) { includedSource = createInputSource(null, href, fCurrentBaseURI.getExpandedSystemId(), accept, acceptLanguage); diff --git a/src/java.xml/share/classes/jdk/xml/internal/AccessRule.java b/src/java.xml/share/classes/jdk/xml/internal/AccessRule.java new file mode 100644 index 00000000000..87af7c23994 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/AccessRule.java @@ -0,0 +1,441 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.xml.internal; + +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; + +/** + * Represents a parsed rule for matching external resource access permissions based on URI patterns. + *

+ * This class encapsulates a resource access rule consisting of a scheme, optional host and port, + * and path pattern. It is used to determine if a specific {@link java.net.URI} is permitted based on + * rules specified with the {@code jdk.xml.resource.access} property. + *

+ *

+ * Supported rule format: + *

+ *   [scheme]://host[:port][/path-pattern]
+ *   [scheme]:/[path-pattern]     (for local schemes such as file, jar, jrt)
+ * 
+ *
    + *
  • scheme: The URI scheme (e.g., http, https, ftp, file, jar, jrt).
  • + *
  • host: Domain name, IPv4, or IPv6 address. For local schemes ("file", "jar", "jrt"), host is omitted.
  • + *
  • port: (optional) Port number to match. If omitted, matches the default port for the scheme.
  • + *
  • path-pattern: (optional) Resource path. Supports wildcards (e.g., {@code /*}, {@code /dtds/*}).
  • + *
+ *

+ * Wildcards are allowed in host (e.g., *.foo.com) and path (e.g., /* or /foo/*). + *

+ *

+ * Example patterns: + *

    + *
  • {@code http://*.foo.com:8080/*} - allows HTTP resources on any subdomain of foo.com at port 8080, any path
  • + *
  • {@code file:/dtds/*} - allows all files under /dtds
  • + *
  • {@code file:/foo/bar.dtd} - allows only the local file /foo/bar.dtd
  • + *
  • {@code *} - allows unrestricted access
  • + *
+ *

+ *

+ * The {@link #allows(java.net.URI)} method determines whether a given URI is permitted according to this rule. + *

+ */ +public class AccessRule { + public static final AccessRule RULE_NONE = new AccessRule(""); + public static final AccessRule RULE_ALL = new AccessRule("*"); + private final List rules = new ArrayList<>(); + private final boolean allowAll; + private final boolean denyAll; + private final String rawInput; + + public AccessRule(String input) { + this.rawInput = input; + String trimmedInput = input == null ? "" : input.trim(); + if (trimmedInput.equals("*")) { + allowAll = true; + denyAll = false; + return; + } else if (trimmedInput.isEmpty()) { + allowAll = false; + denyAll = true; + return; + } + allowAll = false; + denyAll = false; + String[] tokens = input.split(","); + for (String rawToken : tokens) { + String token = checkToken(input, rawToken); + rules.add(URIPatternRule.parse(token)); + } + } + + private String checkToken(String input, String rawToken) { + String token = rawToken.trim(); + if (token.isEmpty() && !rules.isEmpty()) { + throw new IllegalArgumentException("Invalid format for the resource.access property: " + + input + ". Empty rule cannot coexist with other rules."); + } else if (token.equals("*") && !rules.isEmpty()) { + throw new IllegalArgumentException("Invalid format for the resource.access property: " + + input + ". All access (*) rule cannot coexist with other rules."); + } + return token; + } + + public boolean allows(URI uri) { + if (denyAll) return false; + if (allowAll) return true; + for (URIPatternRule rule : rules) { + if (rule.matches(uri)) return true; + } + return false; + } + + @Override + public String toString() { return rawInput; } + + /** + * Represents a parsed URI-based pattern rule. + */ + public static class URIPatternRule { + private final String scheme; + private final HostPattern hostPattern; + private final Integer port; // null if not set + private final PathPattern pathPattern; + // for scheme==jar + private final URIPatternRule jarBaseRule; + private final PathPattern jarEntryPathPattern; + + // Constructor for schemes other than jar + private URIPatternRule(String scheme, HostPattern hostPattern, Integer port, PathPattern pathPattern) { + this.scheme = scheme; + this.hostPattern = hostPattern; + this.port = port; + this.pathPattern = pathPattern; + this.jarBaseRule = null; + this.jarEntryPathPattern = null; + } + + // Constructor for jar patterns + public URIPatternRule(URIPatternRule jarBaseRule, PathPattern jarEntryPathPattern) { + this.scheme = "jar"; + this.hostPattern = null; + this.port = null; + this.pathPattern = null; + this.jarBaseRule = jarBaseRule; + this.jarEntryPathPattern = jarEntryPathPattern; + } + + /** + * Parses the specified pattern string. + * Example patterns: file:*, file:/foo, http://foo.com, https://*.foo.com:8080 + * @param pattern the pattern string + * @return an instance of URIPatternRule from the pattern string + */ + public static URIPatternRule parse(String pattern) { + // Syntax: [scheme]:/{0,3}[host[:port]][/path-pattern] + int schemeSep = pattern.indexOf(':'); + if (schemeSep <= 0) + throw new IllegalArgumentException("Missing or invalid scheme in resource access pattern: " + pattern); + + String scheme = pattern.substring(0, schemeSep).toLowerCase(Locale.ROOT); + if (!isSupportedScheme(scheme)) + throw new IllegalArgumentException("Unsupported scheme in resource access pattern: " + pattern); + + if ("jar".equals(scheme)) { + int exclIdx = pattern.indexOf('!', scheme.length() + 1); + String nestedPart, entryPart; + boolean isLocalScheme = false; + if (exclIdx < 0) { + nestedPart = pattern.substring(scheme.length() + 1); // after "jar:" + entryPart = null; + isLocalScheme = nestedPart.startsWith("file") || nestedPart.startsWith("jar") || nestedPart.startsWith("jrt"); + } else { + nestedPart = pattern.substring(scheme.length() + 1, exclIdx); + entryPart = pattern.substring(exclIdx + 1); + } + + // Check for illegal forms + if (Utils.isEmpty(nestedPart) && Utils.isNotEmpty(entryPart)) + throw new IllegalArgumentException("Invalid JAR rule: entry pattern present but missing jar base: " + pattern); + if (!isLocalScheme && Utils.isEmpty(entryPart) && Utils.isNotEmptyOrWildcard(nestedPart)) + throw new IllegalArgumentException("Invalid JAR rule: entry pattern is empty: " + pattern); + + URIPatternRule jarBaseRule = null; + if (Utils.isNotEmptyOrWildcard(nestedPart)) { + jarBaseRule = URIPatternRule.parse(nestedPart); + } + PathPattern jarEntryPathPattern = (entryPart == null || entryPart.isEmpty() || entryPart.equals("/*") || entryPart.equals("*")) + ? PathPattern.of("*") + : PathPattern.of(entryPart.startsWith("/") ? entryPart : "/" + entryPart); + + return new URIPatternRule(jarBaseRule, jarEntryPathPattern); + } + + String rest = pattern.substring(schemeSep + 1); + // Remove up to 3 leading slashes + String afterSlashes; + + HostPattern hostPattern = null; + Integer port = null; + PathPattern pathPattern = null; + // Handle file/jar/jrt schemes as path patterns + boolean isLocalScheme = scheme.equals("file") || scheme.equals("jar") || scheme.equals("jrt"); + if (isLocalScheme) { + // Remove up to 3 leading slashes + afterSlashes = rest.replaceFirst("^/{0,3}", ""); + // Should be only path or wildcard, must not be empty (file: is not allowed) + if (afterSlashes.isEmpty()) + throw new IllegalArgumentException(scheme + " rule must have non-empty path: " + pattern); + // afterSlashes is the path pattern, can be "*" + if (afterSlashes.equals("*")) { + pathPattern = PathPattern.of("*"); + } else { + if (!afterSlashes.startsWith("/")) afterSlashes = "/" + afterSlashes; + pathPattern = PathPattern.of(afterSlashes); + } + } else { + // Remove up to 2 leading slashes + afterSlashes = rest.replaceFirst("^/{0,2}", ""); + // Find "host[:port][/path]" + if (afterSlashes.isEmpty() || afterSlashes.startsWith(":") || afterSlashes.startsWith("/")) { + throw new IllegalArgumentException("Rule for scheme '" + scheme + "' must specify a non-empty host: " + pattern); + } + + String hostPart; + String pathPart = null; + int slashIndex = afterSlashes.indexOf('/'); + if (slashIndex >= 0) { + hostPart = afterSlashes.substring(0, slashIndex); + pathPart = afterSlashes.substring(slashIndex); // includes "/" + } else { + hostPart = afterSlashes; + } + // Validate host + if (hostPart.isEmpty()) + throw new IllegalArgumentException("Host must not be blank for scheme: " + scheme); + // Port + int portSep = hostPart.lastIndexOf(':'); + if (portSep > 0 && portSep < hostPart.length() - 1 + && isPortNumber(hostPart.substring(portSep + 1))) { + hostPattern = HostPattern.of(hostPart.substring(0, portSep)); + try { + port = Integer.parseInt(hostPart.substring(portSep + 1)); + if (port < 0 || port > 65535) + throw new IllegalArgumentException("Port must be 0-65535: " + pattern); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Port must be numeric: " + pattern); + } + } else if (!hostPart.isEmpty()) { + hostPattern = HostPattern.of(hostPart); + } + + // Validate path + if (pathPart != null && pathPart.length() > 1 && pathPart.indexOf("//") >= 0) + throw new IllegalArgumentException("Path component must not contain empty segments: " + pattern); + + // pathPattern is set only if specified, null otherwise + if (pathPart != null && !pathPart.isEmpty() && !pathPart.equals("/*")) { + pathPattern = PathPattern.of(pathPart); + } else if (pathPart != null && (pathPart.isEmpty() || pathPart.equals("/*"))) { + pathPattern = PathPattern.of("*"); + } + } + return new URIPatternRule(scheme, hostPattern, port, pathPattern); + } + + public boolean matches(URI uri) { + if (uri == null) return false; + String testScheme = uri.getScheme(); + if (testScheme == null || !testScheme.equalsIgnoreCase(scheme)) return false; + + if ("jar".equalsIgnoreCase(testScheme)) { + String ssp = uri.getSchemeSpecificPart(); + int exclIdx = ssp.indexOf("!/"); + String basePart, entryPart; + if (exclIdx < 0) { + basePart = ssp; + entryPart = ""; + } else { + basePart = ssp.substring(0, exclIdx); + entryPart = ssp.substring(exclIdx + 1); // may be empty + } + URI baseUri = URI.create(basePart); + boolean baseMatches = (jarBaseRule == null) || jarBaseRule.matches(baseUri); // wildcard/null means match any + boolean entryMatches = jarEntryPathPattern == null || jarEntryPathPattern.matches(entryPart); // always at least "/*" + return baseMatches && entryMatches; + } + + // Local: path-pattern match only + if (hostPattern == null) { + if (pathPattern == null) return true; // match all local of that scheme + String uriPath = uri.getPath(); + return pathPattern.matches(uriPath); + } + + // Network: host and port required + String testHost = uri.getHost(); + if (!hostPattern.matches(testHost)) return false; + if (port != null && port != (uri.getPort() == -1 ? getDefaultPort(scheme) : uri.getPort())) return false; + // If a pathPattern is present, also match path; else, path is ignored + if (pathPattern != null) { + String uriPath = uri.getPath(); + return pathPattern.matches(uriPath); + } + return true; + } + + private static boolean isSupportedScheme(String scheme) { + return switch (scheme) { + case "http", "https", "ftp", "file", "jar", "jrt" -> true; + default -> false; + }; + } + + /** + * Check if string is an integer between 0 and 65535 (valid TCP/UDP port range). + */ + private static boolean isPortNumber(String str) { + try { + int port = Integer.parseInt(str); + return port >= 0 && port <= 65535; + } catch (NumberFormatException e) { + return false; + } + } + + // standard ports for known schemes + private static int getDefaultPort(String scheme) { + return switch (scheme) { + case "http" -> 80; + case "https" -> 443; + case "ftp" -> 21; + default -> -1; + }; + } + } + + + // Host pattern matching for exact, IPv4 and IPv6 hosts. + public static class HostPattern { + private final String pattern; + private final boolean isAny; + private final boolean isSubdomainPattern; + private final byte[] literalAddress; + + private HostPattern(String pattern, boolean isAny, boolean isSubdomainPattern, byte[] literalAddress) { + this.pattern = pattern; + this.isAny = isAny; + this.isSubdomainPattern = isSubdomainPattern; + this.literalAddress = literalAddress; + } + + public static HostPattern of(String hostPattern) { + String trimmed = hostPattern.trim(); + if (trimmed.equals("*")) { + return new HostPattern("*", true, false, null); + } + if (trimmed.startsWith("*.")) { + // *.example.com + return new HostPattern(trimmed.substring(2).toLowerCase(Locale.ROOT), false, true, null); + } + byte[] literalAddress = parseLiteralAddress(trimmed); + if (literalAddress != null) + return new HostPattern(trimmed, false, false, literalAddress); + // Otherwise, treat as literal domain + return new HostPattern(trimmed.toLowerCase(Locale.ROOT), false, false, null); + } + + public boolean matches(String testHost) { + if (isAny) return true; + if (testHost == null) return false; + if (literalAddress != null) { + byte[] testAddress = parseLiteralAddress(testHost); + return testAddress != null && Arrays.equals(literalAddress, testAddress); + } + testHost = testHost.toLowerCase(Locale.ROOT); + // Subdomain wildcard + if (isSubdomainPattern) { + return testHost.endsWith("." + pattern); + } + // Exact match (domain, IPv4, or IPv6) + return testHost.equals(pattern); + } + + private static byte[] parseLiteralAddress(String host) { + try { + return Inet4Address.ofLiteral(host).getAddress(); + } catch (IllegalArgumentException ignored) { + // Not an IPv4 literal; try IPv6 below. + } + + try { + return Inet6Address.ofLiteral(host).getAddress(); + } catch (IllegalArgumentException ignored) { + return null; + } + } + + } + + public static class PathPattern { + private final String pattern; // E.g. /dtds/* or /* + private final boolean isAny; + private final boolean isDirectory; // endsWith /* + + private PathPattern(String pattern, boolean isAny, boolean isDirectory) { + this.pattern = pattern; + this.isAny = isAny; + this.isDirectory = isDirectory; + } + + public static PathPattern of(String pattern) { + pattern = (pattern == null || pattern.isEmpty()) ? "/" : pattern; + // supports *, /foo/*, /foo/bar + if (pattern.equals("*") || pattern.equals("/*")) { + return new PathPattern(pattern, true, false); + } + if (pattern.endsWith("/*")) { + return new PathPattern(pattern.substring(0, pattern.length() - 2), false, true); + } + return new PathPattern(pattern, false, false); + } + + public boolean matches(String testPath) { + if (isAny) return true; + if (testPath == null) return false; + if (isDirectory) { + // Path starts with this directory + return testPath.startsWith(pattern + "/") || testPath.equals(pattern); + } + return testPath.equals(pattern); + } + } +} diff --git a/src/java.xml/share/classes/jdk/xml/internal/FeaturePropertyBase.java b/src/java.xml/share/classes/jdk/xml/internal/FeaturePropertyBase.java index 0a17ffa1121..d311b778723 100644 --- a/src/java.xml/share/classes/jdk/xml/internal/FeaturePropertyBase.java +++ b/src/java.xml/share/classes/jdk/xml/internal/FeaturePropertyBase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,23 +24,12 @@ */ package jdk.xml.internal; +import jdk.xml.internal.JdkProperty.State; /** * This is the base class for features and properties */ public abstract class FeaturePropertyBase { - - /** - * States of the settings of a property, in the order: default value, value - * set by FEATURE_SECURE_PROCESSING, jaxp.properties file, jaxp system - * properties, and jaxp api properties - */ - public static enum State { - //this order reflects the overriding order - DEFAULT, FSP, JAXPDOTPROPERTIES, SYSTEMPROPERTY, APIPROPERTY - } - - /** * Values of the properties as defined in enum Properties */ diff --git a/src/java.xml/share/classes/jdk/xml/internal/JdkConstants.java b/src/java.xml/share/classes/jdk/xml/internal/JdkConstants.java index 412eddb5ab0..927d19bc3b3 100644 --- a/src/java.xml/share/classes/jdk/xml/internal/JdkConstants.java +++ b/src/java.xml/share/classes/jdk/xml/internal/JdkConstants.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -311,6 +311,17 @@ public final class JdkConstants { //public static final int IGNORE = 1; // same as that of DTD public static final int STRICT = 2; + /** + * System Property for the Resource Access property + * @since 26 + */ + public static final String RESOURCE_ACCESS = "jdk.xml.resource.access"; + + // Integer Values that maps to broad access settings such as *, remote + public static final int ACCESS_NONE = 0; + public static final int ACCESS_ALL = 1; // same as that of DTD + public static final int ACCESS_REMOTE = 2; + /** * Values for a feature */ diff --git a/src/java.xml/share/classes/jdk/xml/internal/JdkXmlUtils.java b/src/java.xml/share/classes/jdk/xml/internal/JdkXmlUtils.java index 9e718b264e4..f66463833ef 100644 --- a/src/java.xml/share/classes/jdk/xml/internal/JdkXmlUtils.java +++ b/src/java.xml/share/classes/jdk/xml/internal/JdkXmlUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -113,7 +113,7 @@ public class JdkXmlUtils { return xsm.setLimit(property, JdkProperty.State.APIPROPERTY, value); } else if (xspm != null && xspm.find(property) != null) { - return xspm.setValue(property, FeaturePropertyBase.State.APIPROPERTY, value); + return xspm.setValue(property, JdkProperty.State.APIPROPERTY, value); } return false; } diff --git a/src/java.xml/share/classes/jdk/xml/internal/SecuritySupport.java b/src/java.xml/share/classes/jdk/xml/internal/SecuritySupport.java index 478d12220d7..2787ba67b5f 100644 --- a/src/java.xml/share/classes/jdk/xml/internal/SecuritySupport.java +++ b/src/java.xml/share/classes/jdk/xml/internal/SecuritySupport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,15 +29,18 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.net.URI; import java.net.URL; import java.net.URLConnection; import java.nio.file.Paths; import java.security.CodeSource; import java.text.MessageFormat; +import java.util.HashSet; import java.util.Locale; import java.util.MissingResourceException; import java.util.Properties; import java.util.ResourceBundle; +import java.util.Set; /** * This class contains utility methods for reading resources in the JAXP packages @@ -267,66 +270,90 @@ public class SecuritySupport { return input; } + /** + * Checks whether access to resource represented by the systemId is permitted + * by Resource Access and/or External Access Properties (EAP) + * @param systemId the systemId + * @param xsm the XMLSecurityManager + * @param eap the External Access Property, e.g. ACCESS_EXTERNAL_DTD + * @param allowedProtocols protocols allowed by the EAP + * @return true if access is permitted, false otherwise + * @throws IOException if the systemId is invalid + */ + public static String checkAccess(String systemId, XMLSecurityManager xsm, + String eap, String allowedProtocols) { + String errMsg = null; + if (xsm != null && !xsm.isAccessAllowed(systemId)) { + errMsg = "Resource Access (jdk.xml.resource.access)"; + } + + if (!checkAccess(systemId, allowedProtocols)) { + errMsg = (errMsg != null) ? errMsg + " and " + eap : eap; + } + return errMsg; + } + /** * Check the protocol used in the systemId against allowed protocols * * @param systemId the Id of the URI * @param allowedProtocols a list of allowed protocols separated by comma - * @param accessAny keyword to indicate allowing any protocol - * @return the name of the protocol if rejected, null otherwise + * @return true if access is permitted, false otherwise */ - public static String checkAccess(String systemId, String allowedProtocols, - String accessAny) throws IOException { + public static boolean checkAccess(String systemId, String allowedProtocols) { + if (Utils.isEmpty(allowedProtocols)) { + return false; + } if (systemId == null || (allowedProtocols != null && - allowedProtocols.equalsIgnoreCase(accessAny))) { - return null; + allowedProtocols.equalsIgnoreCase(JdkConstants.ACCESS_EXTERNAL_ALL))) { + return true; } - String protocol; - if (!systemId.contains(":")) { - protocol = "file"; + URI uri = Utils.createURI(systemId); + String scheme = uri.getScheme(); + if (scheme == null) { + scheme = "file"; } else { - @SuppressWarnings("deprecation") - URL url = new URL(systemId); - protocol = url.getProtocol(); - if (protocol.equalsIgnoreCase("jar")) { - String path = url.getPath(); - protocol = path.substring(0, path.indexOf(":")); - } else if (protocol.equalsIgnoreCase("jrt")) { - // if the systemId is "jrt" then allow access if "file" allowed - protocol = "file"; + scheme = scheme.toLowerCase(Locale.ROOT); + if ("jar".equals(scheme)) { + String ssp = uri.getSchemeSpecificPart(); // e.g. file:/x.jar!/a.xml + int sep = ssp.indexOf("!/"); + if (sep != -1) { + URI nested = Utils.createURI(ssp.substring(0, sep)); + String nestedScheme = nested.getScheme(); + if (nestedScheme != null) { + scheme = nestedScheme.toLowerCase(Locale.ROOT); + } + } + } else if ("jrt".equals(scheme)) { + // allow access if it's "file" + scheme = "file"; } } - if (isProtocolAllowed(protocol, allowedProtocols)) { - //access allowed - return null; - } else { - return protocol; - } + Set allowed = parseProtocols(allowedProtocols); + return allowed.contains(scheme); } /** - * Check if the protocol is in the allowed list of protocols. The check - * is case-insensitive while ignoring whitespaces. - * - * @param protocol a protocol - * @param allowedProtocols a list of allowed protocols - * @return true if the protocol is in the list + * Parses allowed protocols. + * @param protocols the protocol setting + * @return a set containing allowed protocols */ - private static boolean isProtocolAllowed(String protocol, String allowedProtocols) { - if (allowedProtocols == null) { - return false; - } - String temp[] = allowedProtocols.split(","); - for (String t : temp) { - t = t.trim(); - if (t.equalsIgnoreCase(protocol)) { - return true; - } - } - return false; - } + private static Set parseProtocols(String protocols) { + Set set = new HashSet<>(); + if (protocols == null || protocols.isEmpty()) { + return set; + } + + for (String p : protocols.split(",")) { + String trimmed = p.trim().toLowerCase(Locale.ROOT); + if (!trimmed.isEmpty()) { + set.add(trimmed); + } + } + return set; + } public static ClassLoader getContextClassLoader() { ClassLoader cl = Thread.currentThread().getContextClassLoader(); diff --git a/src/java.xml/share/classes/jdk/xml/internal/Utils.java b/src/java.xml/share/classes/jdk/xml/internal/Utils.java index 96d9b1b9521..f7f5c49387b 100644 --- a/src/java.xml/share/classes/jdk/xml/internal/Utils.java +++ b/src/java.xml/share/classes/jdk/xml/internal/Utils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,8 @@ package jdk.xml.internal; import java.lang.reflect.Array; +import java.net.URI; +import java.nio.file.Path; import java.util.Arrays; import java.util.Objects; import java.util.function.Supplier; @@ -132,6 +134,52 @@ public class Utils { * @return {@code true} if the CharSequence is empty or null */ public static boolean isEmpty(final CharSequence cs) { - return cs == null || cs.length() == 0; + return cs == null || cs.isEmpty(); + } + + /** + * Checks if a CharSequence is not null and empty (""). + * @param cs the CharSequence to check, may be null + * @return {@code true} if the CharSequence is not null and empty ("") + */ + public static boolean isNotEmpty(final CharSequence cs) { + return cs != null && !cs.isEmpty(); + } + + /** + * Checks if a CharSequence is not null and empty (""), and also does not end + * with a wildcard (*). + * @param cs the CharSequence to check, may be null + * @return {@code true} if the CharSequence is not null and empty ("") and does + * not end with a wildcard + */ + public static boolean isNotEmptyOrWildcard(final CharSequence cs) { + return cs != null && !cs.isEmpty() && (cs.charAt(cs.length() - 1) != '*'); + } + + /** + * Creates a {@link URI} instance from a systemId. + * This method handles strings that are either absolute URIs or local file + * system paths. + * + * @param systemId the systemId + * @return a {@link URI} instance corresponding to the systemId + */ + public static URI createURI(String systemId) { + if (systemId == null) { + return null; + } + + try { + URI uri = new URI(systemId); + + if (uri.getScheme() == null) { + return Path.of(systemId).toUri(); + } + return uri; + } catch (Exception e) { + // fallback for paths with illegal characters (e.g. spaces) + return Path.of(systemId).toUri(); + } } } diff --git a/src/java.xml/share/classes/jdk/xml/internal/XMLSecurityManager.java b/src/java.xml/share/classes/jdk/xml/internal/XMLSecurityManager.java index a1687c420c3..336b101f8ac 100644 --- a/src/java.xml/share/classes/jdk/xml/internal/XMLSecurityManager.java +++ b/src/java.xml/share/classes/jdk/xml/internal/XMLSecurityManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,8 @@ package jdk.xml.internal; import com.sun.org.apache.xerces.internal.util.SecurityManager; + +import java.net.URI; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -82,6 +84,17 @@ public final class XMLSecurityManager implements Cloneable { CR_MAP = Collections.unmodifiableMap(map); } + // Valid values for Resource Access, and mappings between the string and + // integer values + static final Map RA_MAP; + // Source Level JDK 8 + static { + Map map = new HashMap<>(); + map.put("*", 0); + map.put("", 2); + RA_MAP = Collections.unmodifiableMap(map); + } + // Value converter for properties of type Boolean private static final BooleanMapper BOOLMAPPER = new BooleanMapper(); @@ -94,6 +107,9 @@ public final class XMLSecurityManager implements Cloneable { // Catalog Resolve value mapper private static final StringMapper CRMAPPER = new StringMapper(CR_MAP); + // Resource Access value mapper + private static final StringMapper RAMAPPER = new StringMapper(RA_MAP); + /** * Limits managed by the security manager */ @@ -129,6 +145,8 @@ public final class XMLSecurityManager implements Cloneable { STAX_SUPPORT_DTD("supportDTD", XMLInputFactory.SUPPORT_DTD, null, null, 1, 1, Processor.PARSER, BOOLMAPPER), JDKCATALOG_RESOLVE("JDKCatalogResolve", JdkConstants.JDKCATALOG_RESOLVE, JdkConstants.JDKCATALOG_RESOLVE, null, JdkConstants.CONTINUE, JdkConstants.CONTINUE, Processor.PARSER, CRMAPPER), + RESOURCE_ACCESS("ResourceAccess", JdkConstants.RESOURCE_ACCESS, JdkConstants.RESOURCE_ACCESS, null, + JdkConstants.ALLOW, JdkConstants.ALLOW, Processor.PARSER, RAMAPPER), ; final String key; @@ -262,6 +280,8 @@ public final class XMLSecurityManager implements Cloneable { private final int indexEntityCountInfo = 10000; private String printEntityCountInfo = ""; + private AccessRule accessRule = null; + /** * Default constructor. Establishes default values for known security * vulnerabilities. @@ -289,6 +309,11 @@ public final class XMLSecurityManager implements Cloneable { states[limit.ordinal()] = State.DEFAULT; } } + if (secureProcessing) { + accessRule = (Limit.RESOURCE_ACCESS.secureValue == JdkConstants.ALLOW) ? AccessRule.RULE_ALL : AccessRule.RULE_NONE; + } else { + accessRule = (Limit.RESOURCE_ACCESS.defaultValue() == JdkConstants.ALLOW) ? AccessRule.RULE_ALL : AccessRule.RULE_NONE; + } } /** @@ -380,6 +405,15 @@ public final class XMLSecurityManager implements Cloneable { * @return the limit's new name if found, null otherwise */ public String find(String propertyName) { + /* + * Access property is unique in its value type. Using the SecurityManager + * infrastructure, but handles differently than other limits + */ + if (Limit.RESOURCE_ACCESS.is(propertyName)) { + return (Limit.RESOURCE_ACCESS.systemProperty != null) + ? Limit.RESOURCE_ACCESS.systemProperty + : Limit.RESOURCE_ACCESS.apiProperty; + } for (Limit limit : Limit.values()) { if (limit.is(propertyName)) { // current spec: new property name == systemProperty @@ -440,6 +474,24 @@ public final class XMLSecurityManager implements Cloneable { * @param value the value of the property */ public void setLimit(Limit limit, State state, Object value) { + /* + * Access property is unique in its value type. Using the SecurityManager + * infrastructure, but handles differently than other limits + */ + if (limit == Limit.RESOURCE_ACCESS) { + int index = limit.ordinal(); + if (state.compareTo(states[index]) >= 0) { + String ruleValue = (String)value; + AccessRule rule = new AccessRule(ruleValue); + int intValue = limit.mapper().toInt(ruleValue); + accessRule = rule; + values[index] = intValue; + states[index] = state; + isSet[index] = true; + } + return; + } + int intValue = limit.mapper().toInt(value); if (intValue < 0) { intValue = 0; @@ -469,6 +521,29 @@ public final class XMLSecurityManager implements Cloneable { } } + public AccessRule getAccessRule() { + return accessRule; + } + + public boolean isAccessAllowed(String systemId) { + URI uri = Utils.createURI(systemId); + if (uri == null) return true; + return isAccessAllowed(uri); + } + + /** + * Checks the RESOURCE_ACCESS property to see if access to the specified + * uri is allowed. + * + * @param uri the specified uri + * @return true if the RESOURCE_ACCESS property is set to allow access to the + * specified uri, false otherwise + */ + public boolean isAccessAllowed(URI uri) { + return accessRule.allows(uri); + } + + /** * Return the value of the specified property * @@ -477,6 +552,10 @@ public final class XMLSecurityManager implements Cloneable { * by this manager, its value shall not be null. */ public String getLimitAsString(String propertyName) { + if (Limit.RESOURCE_ACCESS.is(propertyName)) { + return accessRule.toString(); + } + int index = getIndex(propertyName); if (index > -1) { return getLimitValueByIndex(index); @@ -620,10 +699,10 @@ public final class XMLSecurityManager implements Cloneable { } /** - * Check against cumulated value + * Check limit against the cumulated value * * @param limit the type of the limit property - * @param size the size (count or length) of the entity + * @param limitAnalyzer the limit analyzer * @return true if the size is over the limit, false otherwise */ public boolean isOverLimit(Limit limit, XMLLimitAnalyzer limitAnalyzer) { @@ -739,7 +818,7 @@ public final class XMLSecurityManager implements Cloneable { try { String value = System.getProperty(sysPropertyName); - if (value != null && !value.equals("")) { + if (isPropertyValuePresent(limit, value)) { setLimit(limit, State.SYSTEMPROPERTY, value); return true; } @@ -760,7 +839,7 @@ public final class XMLSecurityManager implements Cloneable { private boolean getPropertyConfig(Limit limit, String sysPropertyName) { try { String value = SecuritySupport.readConfig(sysPropertyName); - if (value != null && !value.equals("")) { + if (isPropertyValuePresent(limit, value)) { setLimit(limit, State.JAXPDOTPROPERTIES, value); return true; } @@ -771,6 +850,10 @@ public final class XMLSecurityManager implements Cloneable { return false; } + private boolean isPropertyValuePresent(Limit limit, String value) { + return value != null && (!value.isEmpty() || limit == Limit.RESOURCE_ACCESS); + } + /** * Convert a value set through setProperty to XMLSecurityManager. * If the value is an instance of XMLSecurityManager, use it to override the default; diff --git a/src/java.xml/share/classes/jdk/xml/internal/XMLSecurityPropertyManager.java b/src/java.xml/share/classes/jdk/xml/internal/XMLSecurityPropertyManager.java index ff9ab621fab..226a5e34fbc 100644 --- a/src/java.xml/share/classes/jdk/xml/internal/XMLSecurityPropertyManager.java +++ b/src/java.xml/share/classes/jdk/xml/internal/XMLSecurityPropertyManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ package jdk.xml.internal; import javax.xml.XMLConstants; +import jdk.xml.internal.JdkProperty.State; /** * This class manages security related properties @@ -161,6 +162,15 @@ public final class XMLSecurityPropertyManager extends FeaturePropertyBase implem return values[property.ordinal()]; } + /** + * {@return the state of the specified property} + * + * @param property the specified property + */ + public State getState(Property property) { + return states[property.ordinal()]; + } + /** * Read from system properties, or those in jaxp.properties */ diff --git a/src/java.xml/share/classes/module-info.java b/src/java.xml/share/classes/module-info.java index 1b4f345eb72..fe9c116f7d2 100644 --- a/src/java.xml/share/classes/module-info.java +++ b/src/java.xml/share/classes/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,8 @@ * questions. */ +import java.net.URI; + /** * Defines the Java APIs for XML Processing (JAXP). * @@ -197,6 +199,14 @@ * * * + * If more than one property affects an aspect of operation, all properties must + * permit the operation in order for it to proceed. For example, both the + * Resource Access property + * and External Access Properties (EAPs) must + * be set to allow in order for a direct fetch of an external resource to be + * carried out. For more details, refer to the + * External Resource Resolution Process. + *

* Using the {@link javax.xml.catalog.CatalogFeatures CatalogFeatures}' RESOLVE * property as an example, the following illustrates how these rules are applied: *

    @@ -403,10 +413,7 @@ * *
      *
    • JDK built-in Catalog - * - *
    • + *
    • External Resource Resolution Process
    • *
    • Implementation Specific Properties *
        *
      • Processor Support
      • @@ -511,23 +518,60 @@ *

        * The catalog is loaded once when the first JAXP processor factory is created. * - *

        External Resource Resolution Process with the built-in Catalog

        - * The JDK creates a {@link javax.xml.catalog.CatalogResolver CatalogResolver} - * with the built-in catalog when needed. This CatalogResolver is used as the - * default external resource resolver. + *

        External Resource Resolution Process

        *

        - * XML processors may use resolvers (such as {@link org.xml.sax.EntityResolver EntityResolver}, - * {@link javax.xml.stream.XMLResolver XMLResolver}, and {@link javax.xml.catalog.CatalogResolver CatalogResolver}) - * to handle external references. In the absence of the user-defined resolvers, - * the JDK XML processors fall back to the default CatalogResolver to attempt to - * find a resolution before making a connection to fetch the resources. The fall-back - * also takes place if a user-defined resolver exists but allows the process to - * continue when unable to resolve the resource. + * The XML processor resolves external resources using the following steps: + *

          + *
        • User-defined resolver, such as {@link org.xml.sax.EntityResolver EntityResolver}, + * {@link javax.xml.stream.XMLResolver XMLResolver}, if registered on the XML factory
        • + *
        • {@link javax.xml.catalog.CatalogResolver CatalogResolver} if registered or + * if a catalog file is provided
        • + *
        • The JDK Built-in Catalog
        • + *
        • Direct fetch
        • + *
        + * *

        - * If the default CatalogResolver is unable to locate a resource, it may signal - * the XML processors to continue processing, or skip the resource, or - * throw a CatalogException. The behavior is configured with the - * {@code jdk.xml.jdkcatalog.resolve} property. + * If a resolver or catalog successfully resolves the resource, the process ends. + * Otherwise, the XML processor evaluates the signal returned to decide whether + * to continue, ignore, or reject the resource. + *

        + * It continues to the next step if: + *

          + *
        • User-defined resolver returns {@code null}
        • + *
        • {@link javax.xml.catalog.CatalogResolver CatalogResolver} returns {@code null} + * and the {@link javax.xml.catalog.CatalogFeatures.Feature#RESOLVE RESOLVE} feature + * is set to {@code continue} + *
        • + *
        • The JDK Built-in Catalog returns {@code null} and its Resolve property + * {@code jdk.xml.jdkcatalog.resolve} is set to {@code continue}
        • + *
        + *
        + * The XML processor then will attempt a direct fetch if the resource is allowed + * by the Resource Access property {@code jdk.xml.resource.access} and + * the External Access Properties. + *
        + *

        + * It ignores the resource and returns an empty source if: + *

          + *
        • User-defined resolver returns an empty source
        • + *
        • {@link javax.xml.catalog.CatalogResolver CatalogResolver} returns an empty source + * and the {@link javax.xml.catalog.CatalogFeatures.Feature#RESOLVE RESOLVE} feature + * is set to {@code ignore} + *
        • + *
        + * + * It terminates and throws an exception if: + *
          + *
        • User-defined resolver throws an exception
        • + *
        • {@link javax.xml.catalog.CatalogResolver CatalogResolver} returns {@code null}, + * the {@link javax.xml.catalog.CatalogFeatures.Feature#RESOLVE RESOLVE} feature + * is {@code strict} + *
        • + *
        • The resource is not allowed by Resource Access property + * {@code jdk.xml.resource.access} or + * External Access Properties + *
        • + *
        * *

        Implementation Specific Properties

        * In addition to the standard JAXP Properties, @@ -871,7 +915,7 @@ * Determines whether extension functions in the Transform API are to be allowed. * The extension functions in the XPath API are not affected by this property. * - * yes + * yes * Boolean * * true or false. True indicates that extension functions are allowed; False otherwise. @@ -994,6 +1038,109 @@ * Method 1 * 22 * + * + * {@systemProperty jdk.xml.resource.access} + * Defines allowed network access to external resources by specifying a list + * of URI patterns, each following the syntax:

        + * {@code scheme:[/][host][:port]/[path pattern]}

        + * Except where this specification explicitly extends or overrides the syntax or + * semantics defined by the {@link java.net.URI URI} class, such as the support for wildcard + * patterns, the definitions of {@link java.net.URI URI} apply.

        + * Where: + *
          + *
        • Component Syntax: A resource access entry is either: + *
            + *
          • + * An exact-match URI, where the scheme, host, port, and path components + * must conform to the syntax requirements of the {@link java.net.URI URI} + * class and the URI is interpreted as a hierarchical URI. + *
          • + *
          • + * A wildcard URI pattern, where the URI syntax is extended to allow + * the wildcard {@code *} in the components explicitly defined by this specification. + * The wildcard matching rules for each supported component are described below. + *
          • + *
          + * For both forms, the scheme and host are case-insensitive, while the path is case-sensitive. + *
        • + *
        • scheme: The URI scheme. Unless the entire pattern is the single wildcard {@code *}, + * the scheme must be one of the supported schemes (http, https, ftp, file, or jrt). + *
        • + *
        • jar: jar URIs are matched against the URI of the underlying JAR file + * using the corresponding rule pattern. For example, the rule pattern + * {@code file:/tmp/foo.jar} permits access to {@code jar:file:/tmp/foo.jar!/dtds/test.dtd}. + * + * Permission to access the JAR file automatically grants access to all entries within + * that JAR; entry-level restrictions are not supported. + *
        • + *
        • / (slash): The slash following the scheme may appear one to three times. + * Two slashes (//) indicate the start of an authority (for example, http://example.com). + * One or three slashes (/ or ///) indicate the start of a local resource path + * (for example, {@code file:/foo/bar.dtd, file:///foo/bar.dtd}). + *
        • + *
        • host: Optional. A domain name, IPv4 address, IPv6 literal, or supported + * wildcard pattern. The wildcard {@code *} denotes any host. A leading wildcard followed + * by a domain, such as {@code*.example.com}, matches all subdomains of {@code example.com}. + * For network schemes, the host component must not be empty.
        • + *
        • IPv6 Literals: IPv6 literals must be enclosed in square brackets, + * as defined by {@link java.net.URI URI}, for example {@code https://[2001:db8::6]}
        • + *
        • port: Optional. A decimal port number, as per the URI standard, + * to indicate only the specified port is permitted. If specified, + * it can not be empty. {@code http://example.com:}, for example, is illegal.
        • + *
        • path pattern: Optional. Specifies a resource path. Wildcard {@code *} + * may be used in the path to match any sequence (e.g., {@code /foo/*} matches + * all resources under {@code /foo/}). For local schemes, paths are typically + * absolute (e.g., {@code file:/dtds/*}). To match all resources under a directory, + * the wildcard {@code *} must be added. Without it, the path is treated as + * a literal file or directory.
        • + *
        • entire pattern: can be a wildcard {@code *} or empty "", which + * represents all access or no access permitted respectively.
        • + *
        • wildcard: The wildcard {@code *} may be used only in the forms + * explicitly defined by this specification: + *
            + *
          • as the entire pattern ({@code *});
          • + *
          • as the entire host component ({@code http://*});
          • + *
          • as a leading wildcard in the host component ({@code http://*.example.com});
          • + *
          • as the final path segment ({@code file:/foo/*}).
          • + *
          + * Any other occurrence of * is treated as a literal character and does not perform wildcard matching. + *
        • + *
        + * Example: + * {@snippet : + * jdk.xml.resource.access = https://*.sun.com, http://www.w3.org, https://127.0.0.1, file:/dtds/, jrt:*, file:/tmp/foo.jar + * } + * This configuration permits access to: + *
          + *
        • https access to any subdomain of sun.com, e.g. java.sun.com
        • + *
        • Resources from specific domain as listed in the example, w3.org, 127.0.0.1
        • + *
        • All local resources under the dtds directory
        • + *
        • Resources from the Java runtime image
        • + *
        • Resources inside a jar file {@code /tmp/foo.jar}
        • + *
        + * The following configuration permits all access:
        + * {@code jdk.xml.resource.access = *}
        + * The following configuration permits no access:
        + * {@code jdk.xml.resource.access = ""} + * + * + * String + * + * A comma-separated list of URL patterns, * (wildcard), or "" (empty string). + * + * {@code *} + * {@code *} + * No [8] + * + * DOM
        + * SAX
        + * StAX
        + * Validation
        + * Transform + * + * Method 1 + * 27 + * * * *

        @@ -1037,6 +1184,11 @@ * These three properties control whether DTDs as a whole shall be processed. When * they are set to deny or ignore, other properties that regulate a part or an * aspect of DTD shall have no effect. + *

        + * [8] In the current release, the state of the {@code jdk.xml.resource.access} + * property does not change when {@link javax.xml.XMLConstants#FEATURE_SECURE_PROCESSING FEATURE_SECURE_PROCESSING} + * (FSP) is enabled. In future releases, it will transition to a restrictive setting + * when FSP is enabled. * *

        Legacy Property Names (deprecated)

        * JDK releases prior to JDK 17 support the use of URI style prefix for properties. diff --git a/src/java.xml/share/conf/jaxp.properties b/src/java.xml/share/conf/jaxp.properties index 2b7136319ce..58c7044af77 100644 --- a/src/java.xml/share/conf/jaxp.properties +++ b/src/java.xml/share/conf/jaxp.properties @@ -95,6 +95,81 @@ jdk.xml.overrideDefaultParser=false # javax.xml.accessExternalStylesheet="all" # # +# XML Resource Access Property: +# +# A comma-separated list of URI patterns, * (wildcard), or "" (empty string), +# each following the syntax: scheme:[/][host][:port]/[path] +# +# Component Syntax: A resource access entry is either: +# - An exact-match URI, where the scheme, host, port, and path components must conform +# to the syntax requirements of the java.net.URI class. +# - A wildcard URI pattern, where the URI syntax is extended to allow the wildcard * +# in the components explicitly defined by this specification. +# +# For both forms, the scheme and host are case-insensitive, while the path is case-sensitive. +# +# scheme: The URI scheme. Unless the entire pattern is the single wildcard *, +# the scheme must be one of the supported schemes (http, https, ftp, file, or jrt). +# +# jar: jar URIs are matched against the URI of the underlying JAR file using the +# corresponding rule pattern. For example, the rule pattern file:/tmp/foo.jar permits +# access to jar:file:/tmp/foo.jar!/dtds/test.dtd. +# +# Permission to access the JAR file automatically grants access to all entries within that JAR; +# entry-level restrictions are not supported. + +# / (slash): The slash following the scheme may appear one to three times. +# Two slashes (//) indicate the start of an authority (for example, http://example.com). +# One or three slashes (/ or ///) indicate the start of a local resource path +# (for example, file:/foo/bar.dtd or file:///foo/bar.dtd). + +# host: Optional. A domain name, IPv4 address, IPv6 literal, or supported wildcard pattern. +# The wildcard * denotes any host. A leading wildcard followed by a domain, +# such as *.example.com, matches all subdomains of example.com. For network schemes, +# the host component must not be empty. +# +# IPv6 Literals: IPv6 literals must be enclosed in square brackets, +# as defined by java.net.URI, for example https://[2001:db8::6]. +# +# port: Optional. A decimal port number, as per the URI standard, to indicate only +# the specified port is permitted. If specified, it can not be empty. +# http://example.com:, for example, is illegal. +# +# path: Optional. Specifies a resource path. Wildcard * may be used in the path +# to match any sequence (e.g., /foo/* matches all resources under /foo/). +# For local schemes, paths are typically absolute (e.g., file:/dtds/*). +# To match all resources under a directory, the wildcard * must be added. +# Without it, the path is treated as a literal file or directory. +# +# entire pattern: can be a wildcard * or empty "", which represents all access +# or no access permitted respectively. +# +# wildcard: The wildcard * may be used only in the forms explicitly defined by this specification: +# - as the entire pattern (*); +# - as the entire host component (http://*); +# - as a leading wildcard in the host component (http://*.example.com); +# - as the final path segment (file:/foo/*). +# +# Any other occurrence of * is treated as a literal character and does not perform wildcard matching. +# +# +# Example: +# jdk.xml.resource.access = https://*.sun.com, http://www.w3.org, https://127.0.0.1, file:/dtds/, jrt:*, file:/tmp/foo.jar +#Examples: +# +# jdk.xml.resource.access = https://*.example.com, http://www.example.com, https://127.0.0.1, \ +# https://127.0.0.1:80, https://[fe80::1%lo0], https://[fe80::1%lo0]:80, file:/dtds/*, jrt:/*, file:/tmp/foo.jar +# +# This configuration permits access to: +# - https access to any subdomain of example.com, e.g. foo.example.com +# - Resources from specific domain as listed in the example, example.com, 127.0.0.1 and [fe80::1%lo0] +# - All local resources under the dtds directory +# - Resources from the Java runtime image +# - Resources from local /tmp/foo.jar +# +# The default setting allows unrestricted access +jdk.xml.resource.access=* +# # Catalog Properties: # # The Catalog API defines four features: FILES, PREFER, DEFER and RESOLVE. diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/access/AccessRuleTest.java b/test/jaxp/javax/xml/jaxp/unittest/common/access/AccessRuleTest.java new file mode 100644 index 00000000000..0513896a7f6 --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/access/AccessRuleTest.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package common.access; + +import jdk.xml.internal.AccessRule; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.net.URI; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + + +/* + * @test + * @bug 8357394 + * @summary Verifies access rules defined by property jdk.xml.resource.access + * @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest /test/lib + * @modules java.xml/jdk.xml.internal + * @run junit/othervm common.access.AccessRuleTest + */ +public class AccessRuleTest { + /** + * Returns test data for testAccessRule. + * Data: rules, URI strings, result (true if allowed, false otherwise) + * @return test data for testAccessRule + */ + private static Stream testData() { + + return Stream.of( + Arguments.of("*", "http://all.access", true), + Arguments.of("", "http://no.access", false), + Arguments.of("http:*; http:/*; http://*", "http://all.http.access", true), + Arguments.of("http://*.oracle.com", "http://subdomains.oracle.com/dtds/example.dtd", true), + Arguments.of("https:*;https:/*;https://*", "https://all.https.access", true), + Arguments.of("https://*.oracle.com", "https://subdomains.oracle.com/dtds/example.dtd", true), + Arguments.of("file:*;file:/*;file://*;file:///*", "file://all.file.access", true), + Arguments.of("http://www.oracle.com", "http://www.oracle.com/dtds/example.dtd", true), + Arguments.of("http://www.oracle.com, http://*.oracle.com", + "http://www.oracle.com/dtds/example.dtd; http://subdomains.oracle.com/dtds/example.dtd", true), + Arguments.of("file:/dtds/dtd1.dtd", "file:/dtds/dtd1.dtd", true), + Arguments.of("file:/dtds/dtd1.dtd, file:/xsds/*", "file:/dtds/dtd1.dtd; file:/xsds/example.xsd", true), + Arguments.of("http://www.oracle.com, file:/dtds/dtd1.dtd, file:/xsds/*", + "http://www.oracle.com/dtds/example.dtd; file:/dtds/dtd1.dtd; file:/xsds/example.xsd", true), + Arguments.of("http://[2001:db8::1]", + "http://[2001:0db8:0000:0000:0000:0000:0000:0001]/dtds/example.dtd; " + + "http://[2001:db8:0:0:0:0:0:1]/dtds/example.dtd", true), + Arguments.of("http://[2001:0db8:0000:0000:0000:0000:0000:0001]", + "http://[2001:db8::1]/dtds/example.dtd", true), + Arguments.of("jrt:*; jrt:/java.xml/*", "jrt:/java.xml/jdk/xml/internal/jdkcatalog/JDKCatalog.xml", true), + Arguments.of("jar:file:/tmp/foo.jar; jar:file:/tmp/foo.jar!/dtds/*", "jar:file:/tmp/foo.jar!/dtds/example.dtd", true), + Arguments.of("jar:*; jar:file:*", "jar:file://all.file.access", true) + ); + } + + /** + * Returns test data for testInvalidRules. + * Data: rules, exception class + * @return test data for testInvalidRules + */ + private static Stream testInvalidInput() { + + return Stream.of( + Arguments.of("http://:8080", IllegalArgumentException.class), + Arguments.of("http:///dtds", IllegalArgumentException.class), + Arguments.of("scheme", IllegalArgumentException.class), + Arguments.of("http", IllegalArgumentException.class), + Arguments.of("http:", IllegalArgumentException.class), + Arguments.of("http://:8080", IllegalArgumentException.class), + Arguments.of("http:///dtds", IllegalArgumentException.class), + Arguments.of("http://example.com, , file:*", IllegalArgumentException.class), + Arguments.of("http://example.com, *, file:*", IllegalArgumentException.class) + ); + } + + /** + * Verifies that the Access External Properties are supported throughout the + * JAXP APIs. + * @param rules the access rules separate by ";" + * @param systemIds system IDs represented as semicolon-separated URI strings + * @param permitted the flag indicating whether the rules permit the resource + * represented by the systemId + * @throws Exception if the test fails due to test configuration issues other + * than the expected result + */ + @ParameterizedTest + @MethodSource("testData") + public void testAccessExternalProperties(String rules, String systemIds, boolean permitted) + throws Exception { + String[] accessRules = rules.split(";"); + for (String rule : accessRules) { + AccessRule accessRule = new AccessRule(rule.trim()); + String[] ids = systemIds.split(";"); + for (String systemId : ids) { + assertEquals(accessRule.allows(URI.create(systemId.trim())), permitted); + } + } + } + + /** + * Verifies that the specified rule is invalid. + * @param rule indicates whether there is a custom resolver + * @param expectedType the expected throw type + * @throws Exception if the test fails other than the expected Exception, which + * would indicate an issue in configuring the test + */ + @ParameterizedTest + @MethodSource("testInvalidInput") + public void testAccessRule(String rule, Class expectedType) throws Exception { + assertThrows(expectedType, () -> parseRules(rule)); + + } + + /** + * Attempts to parse an access rule + * @param rule the access rule + + * @throws Exception if the test fails due to test configuration issues other + * than the expected result + */ + private void parseRules(String rule) { + AccessRule accessRule = new AccessRule(rule.trim()); + } +} diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/access/AccessTestBase.java b/test/jaxp/javax/xml/jaxp/unittest/common/access/AccessTestBase.java new file mode 100644 index 00000000000..7b6c0423209 --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/access/AccessTestBase.java @@ -0,0 +1,447 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package common.access; + +import common.util.TestBase; + +import javax.xml.XMLConstants; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.SAXParser; +import javax.xml.stream.XMLInputFactory; +import javax.xml.transform.TransformerFactory; +import javax.xml.validation.SchemaFactory; + +/** + * Test base for Access Rule test. + */ +public class AccessTestBase extends TestBase { + /* + * Test scenarios for verifying preferences. + * + * Fields: + * file, FSP, state of setting, config file, system property, api property, + * Custom Catalog, error expected, error code or expected result + */ + public Object[][] getConfigs(Processor processor) { + // file with an external DTD that's not in JdkCatalog + String fileDTDNotInC = "properties1.xml"; + // file with an external DTD that's in the Custom Catalog + String fileDTDInCC = "test.xml"; + // file with an external DTD that's in JdkCatalog + String javaDTD = "properties.xml"; + + // error code when CATALOG=strict; The cause for DOM + String errCode = "JAXP09040001"; + + // error (not from catalog) is expect when CATALOG=continue + boolean isErrExpected = true; + String expected1 = UNKNOWN_HOST; + + // expected when reference is resolved by Catalog + String expected2 = ""; + switch (processor) { + case SAX: + errCode = "JAXP00090001"; + break; + case STAX: + errCode = "JAXP00090001"; + // StAX is non-validating parser + isErrExpected = false; + expected1 = ".*[\\w\\s]*(value1)[\\w\\s]*.*"; + expected2 = ".*(123)[\\w\\s]*.*"; + break; + default: + break; + } + + return new Object[][]{ + /** + * Case 1: all properties must permit + * Note: External reference not in the built-in catalog + * Expect: error as the parser continues and tries to access an invalid site + * java.net.UnknownHostException: invalid.site.com + */ + /** + * Case 1-1: by default, both RESOURCE_ACCESS and EAP allow access + */ + {fileDTDNotInC, null, null, null, null, null, null, isErrExpected, expected1}, + /** + * Case 1-2: FSP is set, both RESOURCE_ACCESS and EAP are set to allow + */ + {fileDTDNotInC, Properties.FSP, PropertyState.CONFIG_FILE_SYSTEM_API, null, null, new Properties[]{Properties.ACCESS0, Properties.AED0}, null, isErrExpected, expected1}, + /** + * Case 2: access is denied unless all properties permit the operation + * + * Sample Error Message: [Fatal Error] properties1.xml:7:11: External DTD: Failed to read external DTD 'properties1.dtd', + * because access is not allowed due to restriction set by 'Resource Access (jdk.xml.resource.access) and http://javax.xml.XMLConstants/property/accessExternalDTD'. + * + */ + /** + * Case 2-1: FSP is set. RESOURCE_ACCESS is unchanged, and EAP denies access + + * Expect: [Fatal Error] access denied by ACCESS_EXTERNAL_DTD + */ + {fileDTDNotInC, Properties.FSP, null, null, null, null, null, true, XMLConstants.ACCESS_EXTERNAL_DTD}, + /** + * Case 2-2: RESOURCE_ACCESS is set to deny access though EAP allows it + + * Expect: [Fatal Error] access denied by RESOURCE_ACCESS + */ + {fileDTDNotInC, Properties.FSP, PropertyState.API, null, null, new Properties[]{Properties.ACCESS1, Properties.AED0}, null, true, SP_ACCESS}, + /** + * Case 2-3: ACCESS_EXTERNAL_DTD is set to deny access though RESOURCE_ACCESS allows it + + * Expect: [Fatal Error] access denied by ACCESS_EXTERNAL_DTD + */ + {fileDTDNotInC, Properties.FSP, PropertyState.API, null, null, new Properties[]{Properties.ACCESS0, Properties.AED2}, null, true, XMLConstants.ACCESS_EXTERNAL_DTD}, + /** + * Case 2-4: System properties override FSP secure values. + * + * Expect: error as the parser continues and tries to access an invalid site + */ + {fileDTDNotInC, Properties.FSP, PropertyState.SYSTEM, null, new Properties[]{Properties.ACCESS0, Properties.AED0}, null, null, isErrExpected, expected1}, + /** + * Case 2-5: API property setting overrides system property setting. + * + * Expect: error as the parser continues and tries to access an invalid site + */ + {fileDTDNotInC, Properties.FSP, PropertyState.CONFIG_FILE_SYSTEM_API, null, new Properties[]{Properties.ACCESS1, Properties.AED0}, new Properties[]{Properties.ACCESS0, Properties.AED0}, null, isErrExpected, expected1}, + /** + * Case 2-6: API property setting overrides system property setting. + * + * Expect: [Fatal Error] access denied by RESOURCE_ACCESS + */ + {fileDTDNotInC, Properties.FSP, PropertyState.CONFIG_FILE_SYSTEM_API, null, new Properties[]{Properties.ACCESS0, Properties.AED0}, new Properties[]{Properties.ACCESS1, Properties.AED0}, null, true, SP_ACCESS}, + + /** + * Case 3: Resolvers and Catalogs take precedence in the resource resolution process + * + * Sample Error Message when access is denied by Catalog's Resolve property: + * [Fatal Error] properties1.xml:7:11: JAXP00090001: The CatalogResolver is enabled with the catalog "JDKCatalog.xml", but a CatalogException is returned. + * org.xml.sax.SAXException: javax.xml.catalog.CatalogException: JAXP09040001: No match found for publicId 'null' and systemId 'http://invalid.site.com/dtd/properties1.dtd'. + * + */ + /** + * Case 3-1: the built-in catalog's Resolve property is set to "strict", both Resource Access (jdk.xml.resource.access) and External Access Properties (EAPs) have no effect, + * regardless of their configured values + * + * Expect: error as access is denied by the Catalog's Resolve property + */ + {fileDTDNotInC, null, PropertyState.CONFIG_FILE_SYSTEM_API, Properties.CONFIG_FILE_CATALOG_STRICT, null, new Properties[]{Properties.ACCESS0, Properties.AED0}, null, true, errCode}, + /** + * Case 3-2: the reference is resolved by the built-in catalog before direct fetch. + * FSP sets EAP to deny direct fetch, but catalog resolution completes first. + * + * Expect: no error + */ + {javaDTD, Properties.FSP, PropertyState.CONFIG_FILE, Properties.CONFIG_FILE_CATALOG_STRICT, null, null, null, false, expected1}, + /** + * Case 3-3: the reference is resolved by a custom catalog before direct fetch. + * FSP sets EAP to deny direct fetch, but catalog resolution completes first. + * + * Expect: no error + */ + {fileDTDInCC, Properties.FSP, PropertyState.CONFIG_FILE, Properties.CONFIG_FILE_CATALOG_STRICT, null, null, CustomCatalog.STRICT, false, expected2} + }; + } + + /* + * Test scenarios for XInclude. XInclude direct fetches are controlled by + * RESOURCE_ACCESS, while resolver/catalog results take precedence. + */ + public Object[][] getXIncludeConfigs(Processor processor) { + String xinclude = "XI_roottest.xml"; + + return new Object[][]{ + /** + * Case 1-1: by default, RESOURCE_ACCESS allows direct XInclude access. + */ + {xinclude, null, null, null, null, null, null, false, ""}, + /** + * Case 1-2: FSP is set, RESOURCE_ACCESS is explicitly set to allow. + */ + {xinclude, Properties.FSP, PropertyState.API, null, null, + new Properties[]{Properties.ACCESS0}, null, false, ""}, + /** + * Case 2-1: RESOURCE_ACCESS is set to deny direct XInclude access. + * + * Expect: access denied by RESOURCE_ACCESS + */ + {xinclude, Properties.FSP, PropertyState.API, null, null, + new Properties[]{Properties.ACCESS1}, null, true, SP_ACCESS}, + /** + * Case 2-2: system properties override FSP. + */ + {xinclude, Properties.FSP, PropertyState.SYSTEM, null, + new Properties[]{Properties.ACCESS0}, null, null, false, ""}, + /** + * Case 2-3: API property setting overrides system property setting. + */ + {xinclude, Properties.FSP, PropertyState.CONFIG_FILE_SYSTEM_API, null, + new Properties[]{Properties.ACCESS1}, + new Properties[]{Properties.ACCESS0}, null, false, ""}, + /** + * Case 2-4: API property setting overrides system property setting. + * + * Expect: access denied by RESOURCE_ACCESS + */ + {xinclude, Properties.FSP, PropertyState.CONFIG_FILE_SYSTEM_API, null, + new Properties[]{Properties.ACCESS0}, + new Properties[]{Properties.ACCESS1}, null, true, SP_ACCESS}, + /** + * Case 3-1: custom catalog resolves the reference before direct fetch. + * FSP sets EAP to deny direct fetch, but catalog resolution completes first. + * + * Expect: no error + */ + {xinclude, Properties.FSP, PropertyState.CONFIG_FILE, + Properties.CONFIG_FILE_CATALOG_STRICT, null, null, + CustomCatalog.STRICT, false, ""}, + }; + } + + /* + * Test scenarios for configuring properties for validation or transform. + * + * Fields: + * xml file, xsd or xsl file, FSP, state of setting, config file, system property, + * api property, Custom Catalog, error expected, error code or expected result + */ + public Object[][] getConfig(String m) { + // Schema Import + String xmlFile = "XSDImport_company.xsd"; + String xsdOrXsl = null; + String expected = ""; + String errCode = "JAXP00090001"; + Properties eapAllow = Properties.AES0; + Properties eapDeny = Properties.AES2; + String eapName = "accessExternalDTD,accessExternalSchema"; + + switch (m) { + case "SchemaTest2": + // Schema Include + xmlFile = "XSDInclude_company.xsd"; + break; + case "Validation": + // Schema Location + xmlFile = "val_test.xml"; + break; + case "Stylesheet": + errCode = "JAXP09040001"; + xmlFile = "XSLDTD.xsl"; + eapAllow = Properties.AED0; + eapDeny = Properties.AED2; + eapName = XMLConstants.ACCESS_EXTERNAL_DTD; + break; + case "Transform": + xmlFile = "XSLPI.xml"; + errCode = "JAXP00090001"; + xsdOrXsl = "" + + "" + + "" + + "" + + "" + + "" + + " "; + eapAllow = Properties.AEX0; + eapDeny = Properties.AEX2; + eapName = XMLConstants.ACCESS_EXTERNAL_STYLESHEET; + break; + default: + break; + } + + return new Object[][]{ + /** + * Case 1: all properties must permit. + * + * Case 1-1: by default, both RESOURCE_ACCESS and EAP allow access. + */ + {xmlFile, xsdOrXsl, null, null, null, null, null, null, false, expected}, + /** + * Case 1-2: FSP is set, both RESOURCE_ACCESS and EAP are set to allow. + */ + {xmlFile, xsdOrXsl, Properties.FSP, PropertyState.CONFIG_FILE_SYSTEM_API, null, null, new Properties[]{Properties.ACCESS0, Properties.AED0, eapAllow}, null, false, expected}, + /** + * Case 2: access is denied unless all properties permit the operation. + * + * Case 2-1: FSP is set. RESOURCE_ACCESS is unchanged, and EAP denies access. + * + * Expect: access denied by EAP + */ + {xmlFile, xsdOrXsl, Properties.FSP, PropertyState.CONFIG_FILE, null, null, null, null, true, eapName}, + /** + * Case 2-2: RESOURCE_ACCESS is set to deny access though EAP allows it. + * + * Expect: access denied by RESOURCE_ACCESS + */ + {xmlFile, xsdOrXsl, Properties.FSP, PropertyState.API, null, null, new Properties[]{Properties.ACCESS1, Properties.AED0, eapAllow}, null, true, SP_ACCESS}, + /** + * Case 2-3: EAP is set to deny access though RESOURCE_ACCESS allows it. + * + * Expect: access denied by EAP + */ + {xmlFile, xsdOrXsl, Properties.FSP, PropertyState.API, null, null, new Properties[]{Properties.ACCESS0, Properties.AED0, eapDeny}, null, true, eapName}, + /** + * Case 2-4: system properties override FSP secure values. + */ + {xmlFile, xsdOrXsl, Properties.FSP, PropertyState.SYSTEM, null, new Properties[]{Properties.ACCESS0, Properties.AED0, eapAllow}, null, null, false, expected}, + /** + * Case 2-5: API property setting overrides system property setting. + */ + {xmlFile, xsdOrXsl, Properties.FSP, PropertyState.CONFIG_FILE_SYSTEM_API, null, new Properties[]{Properties.ACCESS1, Properties.AED0, eapAllow}, new Properties[]{Properties.ACCESS0, Properties.AED0, eapAllow}, null, false, expected}, + /** + * Case 2-6: API property setting overrides system property setting. + * + * Expect: access denied by RESOURCE_ACCESS + */ + {xmlFile, xsdOrXsl, Properties.FSP, PropertyState.CONFIG_FILE_SYSTEM_API, null, new Properties[]{Properties.ACCESS0, Properties.AED0, eapAllow}, new Properties[]{Properties.ACCESS1, Properties.AED0, eapAllow}, null, true, SP_ACCESS}, + /** + * Case 3: Catalogs take precedence in the resource resolution process. + * + * Case 3-1: the built-in catalog's Resolve property is set to "strict", + * and the reference is not in the built-in catalog. + * + * Expect: error as access is denied by the Catalog's Resolve property + */ + {xmlFile, xsdOrXsl, null, PropertyState.CONFIG_FILE_SYSTEM_API, Properties.CONFIG_FILE_CATALOG_STRICT, null, new Properties[]{Properties.ACCESS0, Properties.AED0, eapAllow}, null, true, errCode}, + /** + * Case 3-2: custom catalog resolves the reference before direct fetch. + * FSP sets EAP to deny direct fetch, but catalog resolution completes first. + * + * Expect: no error + */ + {xmlFile, xsdOrXsl, Properties.FSP, PropertyState.CONFIG_FILE, Properties.CONFIG_FILE_CATALOG_STRICT, null, null, CustomCatalog.STRICT, false, expected}, + }; + } + + public void testDOM(String filename, Properties fsp, PropertyState state, + Properties config, Properties[] sysProp, Properties[] apiProp, CustomCatalog cc, + boolean expectError, String error) throws Exception { + + DocumentBuilderFactory dbf = getDBF(fsp, state, config, sysProp, apiProp, cc); + process(filename, dbf, expectError, error); + } + + public void testSAX(String filename, Properties fsp, PropertyState state, + Properties config, Properties[] sysProp, Properties[] apiProp, CustomCatalog cc, + boolean expectError, String error) throws Exception { + + SAXParser parser = getSAXParser(fsp, state, config, sysProp, apiProp, cc); + process(filename, parser, expectError, error); + } + + public void testStAX(String filename, Properties fsp, PropertyState state, + Properties config, Properties[] sysProp, Properties[] apiProp, CustomCatalog cc, + boolean expectError, String error) throws Exception { + + XMLInputFactory xif = getXMLInputFactory(state, config, sysProp, apiProp, cc); + process(filename, xif, expectError, error); + } + + public void testSchema1(String filename, String xsd, Properties fsp, PropertyState state, + Properties config, Properties[] sysProp, Properties[] apiProp, CustomCatalog cc, + boolean expectError, String error) throws Exception { + + SchemaFactory sf = getSchemaFactory(fsp, state, config, sysProp, apiProp, cc); + process(filename, sf, expectError, error); + } + + public void testSchema2(String filename, String xsd, Properties fsp, PropertyState state, + Properties config, Properties[] sysProp, Properties[] apiProp, CustomCatalog cc, + boolean expectError, String error) throws Exception { + testSchema1(filename, xsd, fsp, state, config, sysProp, apiProp, cc, expectError, error); + } + + public void testValidation(String filename, String xsd, Properties fsp, PropertyState state, + Properties config, Properties[] sysProp, Properties[] apiProp, CustomCatalog cc, + boolean expectError, String error) throws Exception { + + SchemaFactory sf = getSchemaFactory(fsp, state, config, sysProp, apiProp, cc); + validate(filename, sf, expectError, error); + } + + public void testStylesheet(String filename, String xsl, Properties fsp, PropertyState state, + Properties config, Properties[] sysProp, Properties[] apiProp, CustomCatalog cc, + boolean expectError, String error) throws Exception { + + TransformerFactory tf = getTransformerFactory(fsp, state, config, sysProp, apiProp, cc); + process(filename, tf, expectError, error); + } + + public void testTransform(String filename, String xsl, Properties fsp, PropertyState state, + Properties config, Properties[] sysProp, Properties[] apiProp, CustomCatalog cc, + boolean expectError, String error) throws Exception { + + TransformerFactory tf = getTransformerFactory(fsp, state, config, sysProp, apiProp, cc); + transform(filename, xsl, tf, expectError, error); + } + + // parameters in the same order as the test method + String filename; String xsd; String xsl; Properties fsp; PropertyState state; + Properties config; Properties[] sysProp; Properties[] apiProp; CustomCatalog cc; + boolean expectError; String error; + + // Maps the scenario array to individual parameters + public void paramMap(Processor processor, String method, String index) { + int i = 0; + Object[][] params; + if (processor == Processor.VALIDATOR || + processor == Processor.TRANSFORMER) { + params = getConfig(method); + i = 1; + } else { + params = getConfigs(processor); + } + Object[] param = params[Integer.parseInt(index)]; + filename = (String)param[0]; + if (processor == Processor.VALIDATOR) { + xsd = (String)param[i]; + } else if (processor == Processor.TRANSFORMER) { + xsl = (String)param[i]; + } + fsp = (Properties)param[i + 1]; + state = (PropertyState)param[i + 2]; + config = (Properties)param[i + 3]; + sysProp = (Properties[])param[i + 4]; + apiProp = (Properties[])param[i + 5]; + cc = (CustomCatalog)param[i + 6]; + expectError = (boolean)param[i + 7]; + error = (String)param[i + 8]; + } + + public void paramMapXInclude(Processor processor, String index) { + Object[] param = getXIncludeConfigs(processor)[Integer.parseInt(index)]; + filename = (String)param[0]; + fsp = (Properties)param[1]; + state = (PropertyState)param[2]; + config = (Properties)param[3]; + sysProp = (Properties[])param[4]; + apiProp = (Properties[])param[5]; + cc = (CustomCatalog)param[6]; + expectError = (boolean)param[7]; + error = (String)param[8]; + } +} diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/access/DOMTest.java b/test/jaxp/javax/xml/jaxp/unittest/common/access/DOMTest.java new file mode 100644 index 00000000000..e0223f93459 --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/access/DOMTest.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package common.access; + +import java.net.ProxySelector; + +/** + * @test @bug 8357394 + * @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest + * @modules java.xml/jdk.xml.internal + * @run driver common.access.DOMTest 0 // by default, both RESOURCE_ACCESS and EAP allow access + * @run driver common.access.DOMTest 1 // FSP is set, both RESOURCE_ACCESS and EAP are set to allow + * @run driver common.access.DOMTest 2 // FSP is set, RESOURCE_ACCESS is unchanged, EAP denies access + * @run driver common.access.DOMTest 3 // RESOURCE_ACCESS denies access though EAP allows it + * @run driver common.access.DOMTest 4 // ACCESS_EXTERNAL_DTD denies access though RESOURCE_ACCESS allows it + * @run driver common.access.DOMTest 5 // system properties override FSP secure values + * @run driver common.access.DOMTest 6 // API RESOURCE_ACCESS allow overrides system RESOURCE_ACCESS deny + * @run driver common.access.DOMTest 7 // API RESOURCE_ACCESS deny overrides system RESOURCE_ACCESS allow + * @run driver common.access.DOMTest 8 // the built-in catalog's Resolve property is strict + * @run driver common.access.DOMTest 9 // the built-in catalog resolves the reference before direct fetch + * @run driver common.access.DOMTest 10 // the custom catalog resolves the reference before direct fetch + * @summary Tests the interaction between Resource Access (jdk.xml.resource.access), + * External Access Properties (EAPs), and the built-in catalog's Resolve setting, + * ensuring correct precedence and behavior across different combinations of these + * configuration mechanisms + */ +public class DOMTest extends AccessTestBase { + public static void main(String[] args) throws Exception { + final ProxySelector previous = ProxySelector.getDefault(); + // disable proxy + ProxySelector.setDefault(ProxySelector.of(null)); + try { + new DOMTest().run(args[0]); + } finally { + // reset to the previous proxy selector + ProxySelector.setDefault(previous); + } + } + + public void run(String index) throws Exception { + paramMap(Processor.DOM, null, index); + super.testDOM(filename, fsp, state, config, sysProp, apiProp, cc, expectError, error); + + } +} diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/access/SAXTest.java b/test/jaxp/javax/xml/jaxp/unittest/common/access/SAXTest.java new file mode 100644 index 00000000000..d17852b895b --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/access/SAXTest.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package common.access; + +import common.util.TestBase; + +import java.net.ProxySelector; + +/** + * @test @bug 8357394 + * @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest + * @modules java.xml/jdk.xml.internal + * @run driver common.access.SAXTest 0 // by default, both RESOURCE_ACCESS and EAP allow access + * @run driver common.access.SAXTest 1 // FSP is set, both RESOURCE_ACCESS and EAP are set to allow + * @run driver common.access.SAXTest 2 // FSP is set, RESOURCE_ACCESS is unchanged, EAP denies access + * @run driver common.access.SAXTest 3 // RESOURCE_ACCESS denies access though EAP allows it + * @run driver common.access.SAXTest 4 // ACCESS_EXTERNAL_DTD denies access though RESOURCE_ACCESS allows it + * @run driver common.access.SAXTest 5 // system properties override FSP secure values + * @run driver common.access.SAXTest 6 // API RESOURCE_ACCESS allow overrides system RESOURCE_ACCESS deny + * @run driver common.access.SAXTest 7 // API RESOURCE_ACCESS deny overrides system RESOURCE_ACCESS allow + * @run driver common.access.SAXTest 8 // the built-in catalog's Resolve property is strict + * @run driver common.access.SAXTest 9 // the built-in catalog resolves the reference before direct fetch + * @run driver common.access.SAXTest 10 // the custom catalog resolves the reference before direct fetch + * @summary Tests the interaction between Resource Access (jdk.xml.resource.access), + * External Access Properties (EAPs), and the built-in catalog's Resolve setting, + * ensuring correct precedence and behavior across different combinations of these + * configuration mechanisms + */ +public class SAXTest extends AccessTestBase { + public static void main(String[] args) throws Exception { + final ProxySelector previous = ProxySelector.getDefault(); + // disable proxy + ProxySelector.setDefault(ProxySelector.of(null)); + try { + new SAXTest().run(args[0]); + } finally { + // reset to the previous proxy selector + ProxySelector.setDefault(previous); + } + } + + public void run(String index) throws Exception { + paramMap(TestBase.Processor.SAX, null, index); + super.testSAX(filename, fsp, state, config, sysProp, apiProp, cc, expectError, error); + + } +} diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/access/SchemaTest.java b/test/jaxp/javax/xml/jaxp/unittest/common/access/SchemaTest.java new file mode 100644 index 00000000000..e984bc16639 --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/access/SchemaTest.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package common.access; + +import java.net.ProxySelector; + +/** + * @test @bug 8306632 + * @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest + * @modules java.xml/jdk.xml.internal + * @run driver common.access.SchemaTest SchemaTest1 0 // default RESOURCE_ACCESS and ACCESS_EXTERNAL_SCHEMA allow access + * @run driver common.access.SchemaTest SchemaTest1 1 // FSP with explicit RESOURCE_ACCESS and ACCESS_EXTERNAL_SCHEMA allow + * @run driver common.access.SchemaTest SchemaTest1 2 // FSP leaves RESOURCE_ACCESS unchanged, ACCESS_EXTERNAL_SCHEMA denies + * @run driver common.access.SchemaTest SchemaTest1 3 // RESOURCE_ACCESS denies access though ACCESS_EXTERNAL_SCHEMA allows it + * @run driver common.access.SchemaTest SchemaTest1 4 // ACCESS_EXTERNAL_SCHEMA denies access though RESOURCE_ACCESS allows it + * @run driver common.access.SchemaTest SchemaTest1 5 // system properties override FSP secure values + * @run driver common.access.SchemaTest SchemaTest1 6 // API RESOURCE_ACCESS allow overrides system RESOURCE_ACCESS deny + * @run driver common.access.SchemaTest SchemaTest1 7 // API RESOURCE_ACCESS deny overrides system RESOURCE_ACCESS allow + * @run driver common.access.SchemaTest SchemaTest1 8 // built-in catalog resolve=strict denies unresolved reference + * @run driver common.access.SchemaTest SchemaTest1 9 // custom catalog resolves before direct fetch + * @run driver common.access.SchemaTest SchemaTest2 0 // default RESOURCE_ACCESS and ACCESS_EXTERNAL_SCHEMA allow access + * @run driver common.access.SchemaTest SchemaTest2 1 // FSP with explicit RESOURCE_ACCESS and ACCESS_EXTERNAL_SCHEMA allow + * @run driver common.access.SchemaTest SchemaTest2 2 // FSP leaves RESOURCE_ACCESS unchanged, ACCESS_EXTERNAL_SCHEMA denies + * @run driver common.access.SchemaTest SchemaTest2 3 // RESOURCE_ACCESS denies access though ACCESS_EXTERNAL_SCHEMA allows it + * @run driver common.access.SchemaTest SchemaTest2 4 // ACCESS_EXTERNAL_SCHEMA denies access though RESOURCE_ACCESS allows it + * @run driver common.access.SchemaTest SchemaTest2 5 // system properties override FSP secure values + * @run driver common.access.SchemaTest SchemaTest2 6 // API RESOURCE_ACCESS allow overrides system RESOURCE_ACCESS deny + * @run driver common.access.SchemaTest SchemaTest2 7 // API RESOURCE_ACCESS deny overrides system RESOURCE_ACCESS allow + * @run driver common.access.SchemaTest SchemaTest2 8 // built-in catalog resolve=strict denies unresolved reference + * @run driver common.access.SchemaTest SchemaTest2 9 // custom catalog resolves before direct fetch + * @run driver common.access.SchemaTest Validation 0 // default RESOURCE_ACCESS and ACCESS_EXTERNAL_SCHEMA allow access + * @run driver common.access.SchemaTest Validation 1 // FSP with explicit RESOURCE_ACCESS and ACCESS_EXTERNAL_SCHEMA allow + * @run driver common.access.SchemaTest Validation 2 // FSP leaves RESOURCE_ACCESS unchanged, ACCESS_EXTERNAL_SCHEMA denies + * @run driver common.access.SchemaTest Validation 3 // RESOURCE_ACCESS denies access though ACCESS_EXTERNAL_SCHEMA allows it + * @run driver common.access.SchemaTest Validation 4 // ACCESS_EXTERNAL_SCHEMA denies access though RESOURCE_ACCESS allows it + * @run driver common.access.SchemaTest Validation 5 // system properties override FSP secure values + * @run driver common.access.SchemaTest Validation 6 // API RESOURCE_ACCESS allow overrides system RESOURCE_ACCESS deny + * @run driver common.access.SchemaTest Validation 7 // API RESOURCE_ACCESS deny overrides system RESOURCE_ACCESS allow + * @run driver common.access.SchemaTest Validation 8 // built-in catalog resolve=strict denies unresolved reference + * @run driver common.access.SchemaTest Validation 9 // custom catalog resolves before direct fetch + * @summary verifies Schema and Validation support for Resource Access, ACCESS_EXTERNAL_SCHEMA, and catalog precedence. + */ +public class SchemaTest extends AccessTestBase { + + public static void main(String args[]) throws Exception { + new SchemaTest().run(args[0], args[1]); + } + + public void run(String method, String index) throws Exception { + paramMap(Processor.VALIDATOR, method, index); + switch (method) { + case "SchemaTest1": + super.testSchema1(filename, xsd, fsp, state, config, sysProp, apiProp, cc, expectError, error); + break; + case "SchemaTest2": + super.testSchema2(filename, xsd, fsp, state, config, sysProp, apiProp, cc, expectError, error); + break; + case "Validation": + super.testValidation(filename, xsd, fsp, state, config, sysProp, apiProp, cc, expectError, error); + break; + } + } +} diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/access/StAXTest.java b/test/jaxp/javax/xml/jaxp/unittest/common/access/StAXTest.java new file mode 100644 index 00000000000..65b73ee03ed --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/access/StAXTest.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package common.access; + + +import common.util.TestBase; + +import java.net.ProxySelector; + +/** + * @test @bug 8357394 + * @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest + * @modules java.xml/jdk.xml.internal + * @run driver common.access.StAXTest 0 // by default, both RESOURCE_ACCESS and EAP allow access + * @run driver common.access.StAXTest 3 // RESOURCE_ACCESS denies access though EAP allows it + * @run driver common.access.StAXTest 4 // ACCESS_EXTERNAL_DTD denies access though RESOURCE_ACCESS allows it + * @run driver common.access.StAXTest 5 // system properties override FSP secure values + * @run driver common.access.StAXTest 6 // API RESOURCE_ACCESS allow overrides system RESOURCE_ACCESS deny + * @run driver common.access.StAXTest 7 // API RESOURCE_ACCESS deny overrides system RESOURCE_ACCESS allow + * @run driver common.access.StAXTest 8 // the built-in catalog's Resolve property is strict + * @run driver common.access.StAXTest 9 // the built-in catalog resolves the reference before direct fetch + * @run driver common.access.StAXTest 10 // the custom catalog resolves the reference before direct fetch + * @summary Tests the interaction between Resource Access (jdk.xml.resource.access), + * External Access Properties (EAPs), and the built-in catalog's Resolve setting, + * ensuring correct precedence and behavior across different combinations of these + * configuration mechanisms + */ +public class StAXTest extends AccessTestBase { + public static void main(String[] args) throws Exception { + new StAXTest().run(args[0]); + } + + public void run(String index) throws Exception { + paramMap(TestBase.Processor.STAX, null, index); + super.testStAX(filename, fsp, state, config, sysProp, apiProp, cc, expectError, error); + + } +} diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/access/TransformTest.java b/test/jaxp/javax/xml/jaxp/unittest/common/access/TransformTest.java new file mode 100644 index 00000000000..38a5ec18b72 --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/access/TransformTest.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package common.access; + +/** + * @test @bug 8306632 + * @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest + * @modules java.xml/jdk.xml.internal + * @run driver common.access.TransformTest Stylesheet 0 // default RESOURCE_ACCESS and ACCESS_EXTERNAL_DTD allow access + * @run driver common.access.TransformTest Stylesheet 1 // FSP with explicit RESOURCE_ACCESS and ACCESS_EXTERNAL_DTD allow + * @run driver common.access.TransformTest Stylesheet 2 // FSP leaves RESOURCE_ACCESS unchanged, ACCESS_EXTERNAL_DTD denies + * @run driver common.access.TransformTest Stylesheet 3 // RESOURCE_ACCESS denies access though ACCESS_EXTERNAL_DTD allows it + * @run driver common.access.TransformTest Stylesheet 4 // ACCESS_EXTERNAL_DTD denies access though RESOURCE_ACCESS allows it + * @run driver common.access.TransformTest Stylesheet 5 // system properties override FSP secure values + * @run driver common.access.TransformTest Stylesheet 6 // API RESOURCE_ACCESS allow overrides system RESOURCE_ACCESS deny + * @run driver common.access.TransformTest Stylesheet 7 // API RESOURCE_ACCESS deny overrides system RESOURCE_ACCESS allow + * @run driver common.access.TransformTest Stylesheet 8 // built-in catalog resolve=strict denies unresolved reference + * @run driver common.access.TransformTest Stylesheet 9 // custom catalog resolves before direct fetch + * @run driver common.access.TransformTest Transform 0 // default RESOURCE_ACCESS and ACCESS_EXTERNAL_STYLESHEET allow access + * @run driver common.access.TransformTest Transform 1 // FSP with explicit RESOURCE_ACCESS and ACCESS_EXTERNAL_STYLESHEET allow + * @run driver common.access.TransformTest Transform 2 // FSP leaves RESOURCE_ACCESS unchanged, ACCESS_EXTERNAL_STYLESHEET denies + * @run driver common.access.TransformTest Transform 3 // RESOURCE_ACCESS denies access though ACCESS_EXTERNAL_STYLESHEET allows it + * @run driver common.access.TransformTest Transform 4 // ACCESS_EXTERNAL_STYLESHEET denies access though RESOURCE_ACCESS allows it + * @run driver common.access.TransformTest Transform 5 // system properties override FSP secure values + * @run driver common.access.TransformTest Transform 6 // API RESOURCE_ACCESS allow overrides system RESOURCE_ACCESS deny + * @run driver common.access.TransformTest Transform 7 // API RESOURCE_ACCESS deny overrides system RESOURCE_ACCESS allow + * @run driver common.access.TransformTest Transform 8 // built-in catalog resolve=strict denies unresolved reference + * @run driver common.access.TransformTest Transform 9 // custom catalog resolves before direct fetch + * @summary verifies Transform support for Resource Access, external access properties, and catalog precedence. + */ +public class TransformTest extends AccessTestBase { + + public static void main(String args[]) throws Exception { + new TransformTest().run(args[0], args[1]); + } + + public void run(String method, String index) throws Exception { + paramMap(Processor.TRANSFORMER, method, index); + switch (method) { + case "Stylesheet": + super.testStylesheet(filename, xsl, fsp, state, config, sysProp, apiProp, cc, expectError, error); + break; + case "Transform": + super.testTransform(filename, xsl, fsp, state, config, sysProp, apiProp, cc, expectError, error); + break; + } + } +} diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/access/XIncludeTest.java b/test/jaxp/javax/xml/jaxp/unittest/common/access/XIncludeTest.java new file mode 100644 index 00000000000..47b5aa6e85a --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/access/XIncludeTest.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package common.access; + +/** + * @test @bug 8306632 + * @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest + * @modules java.xml/jdk.xml.internal + * @run driver common.access.XIncludeTest DOM 0 // default RESOURCE_ACCESS allows direct XInclude access + * @run driver common.access.XIncludeTest DOM 1 // FSP with explicit RESOURCE_ACCESS allow + * @run driver common.access.XIncludeTest DOM 2 // RESOURCE_ACCESS denies direct XInclude access + * @run driver common.access.XIncludeTest DOM 3 // system properties override FSP + * @run driver common.access.XIncludeTest DOM 4 // API RESOURCE_ACCESS allow overrides system RESOURCE_ACCESS deny + * @run driver common.access.XIncludeTest DOM 5 // API RESOURCE_ACCESS deny overrides system RESOURCE_ACCESS allow + * @run driver common.access.XIncludeTest DOM 6 // custom catalog resolves before direct fetch + * @run driver common.access.XIncludeTest SAX 0 // default RESOURCE_ACCESS allows direct XInclude access + * @run driver common.access.XIncludeTest SAX 1 // FSP with explicit RESOURCE_ACCESS allow + * @run driver common.access.XIncludeTest SAX 2 // RESOURCE_ACCESS denies direct XInclude access + * @run driver common.access.XIncludeTest SAX 3 // system properties override FSP + * @run driver common.access.XIncludeTest SAX 4 // API RESOURCE_ACCESS allow overrides system RESOURCE_ACCESS deny + * @run driver common.access.XIncludeTest SAX 5 // API RESOURCE_ACCESS deny overrides system RESOURCE_ACCESS allow + * @run driver common.access.XIncludeTest SAX 6 // custom catalog resolves before direct fetch + * @summary verifies XInclude support for Resource Access and catalog precedence. + */ +public class XIncludeTest extends AccessTestBase { + + public static void main(String args[]) throws Exception { + new XIncludeTest().run(args[0], args[1]); + } + + public void run(String processor, String index) throws Exception { + Processor p = Processor.valueOf(processor); + paramMapXInclude(p, index); + switch (p) { + case DOM: + super.testDOM(filename, fsp, state, config, sysProp, apiProp, cc, expectError, error); + break; + case SAX: + super.testSAX(filename, fsp, state, config, sysProp, apiProp, cc, expectError, error); + break; + default: + throw new IllegalArgumentException("Unsupported processor: " + processor); + } + } +} diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/TestCatalog.xml b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/TestCatalog.xml index 216b3b81543..f1912ed877a 100644 --- a/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/TestCatalog.xml +++ b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/TestCatalog.xml @@ -18,6 +18,9 @@ + + + @@ -26,7 +29,7 @@ - diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/dtd/DTDTestBase.java b/test/jaxp/javax/xml/jaxp/unittest/common/dtd/DTDTestBase.java index b37b7da4e9a..81ac14ce9e9 100644 --- a/test/jaxp/javax/xml/jaxp/unittest/common/dtd/DTDTestBase.java +++ b/test/jaxp/javax/xml/jaxp/unittest/common/dtd/DTDTestBase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */ package common.dtd; @@ -12,7 +12,7 @@ import javax.xml.validation.SchemaFactory; import common.util.TestBase; /** - * @bug 8306632 + * @bug 8306632 8357394 * @summary tests the DTD property jdk.xml.dtd.support. * The DTD property controls how DTDs are processed. */ @@ -126,7 +126,7 @@ public class DTDTestBase extends TestBase { * Expect: error as the parser processes DTD and tries to access the invalid site * Error: JAXP00010008 java.net.UnknownHostException: invalid.site.com */ - {fileDTDNotInC, null, null, null, null, null, isErrExpected, expected}, + {fileDTDNotInC, null, null, null, null, null, isErrExpected, expected1}, /** * Case 1-2: DTD=deny in config file diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/util/TestBase.java b/test/jaxp/javax/xml/jaxp/unittest/common/util/TestBase.java index 77672609147..9e0a629a217 100644 --- a/test/jaxp/javax/xml/jaxp/unittest/common/util/TestBase.java +++ b/test/jaxp/javax/xml/jaxp/unittest/common/util/TestBase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,7 +29,6 @@ import java.io.InputStream; import java.io.StringReader; import java.io.StringWriter; import java.util.Objects; -import java.util.regex.Pattern; import javax.xml.XMLConstants; import javax.xml.catalog.CatalogFeatures; import javax.xml.parsers.DocumentBuilder; @@ -44,6 +43,7 @@ import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import javax.xml.transform.Source; import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.sax.SAXSource; @@ -53,8 +53,8 @@ import javax.xml.transform.stream.StreamSource; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import javax.xml.validation.Validator; -import org.w3c.dom.Document; import org.xml.sax.InputSource; +import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; /** @@ -90,8 +90,8 @@ public class TestBase { private static final String CONFIG_FILE = "java.xml.config.file"; // CATALOG Abbreviation: C - static final String C_FILE = CatalogFeatures.Feature.FILES.getPropertyName(); - static final String C_RESOLVE = CatalogFeatures.Feature.RESOLVE.getPropertyName(); + public static final String C_FILE = CatalogFeatures.Feature.FILES.getPropertyName(); + public static final String C_RESOLVE = CatalogFeatures.Feature.RESOLVE.getPropertyName(); // Xerces Property public static final String DISALLOW_DTD = "http://apache.org/xml/features/disallow-doctype-decl"; @@ -106,15 +106,31 @@ public class TestBase { public static final String SP_CATALOG = "jdk.xml.jdkcatalog.resolve"; public static final String OVERRIDE_PARSER = "jdk.xml.overrideDefaultParser"; + //System Properties corresponding to ACCESS_EXTERNAL_* properties + public static final String SP_ACCESS_EXTERNAL_STYLESHEET = "javax.xml.accessExternalStylesheet"; + public static final String SP_ACCESS_EXTERNAL_DTD = "javax.xml.accessExternalDTD"; + public static final String SP_ACCESS_EXTERNAL_SCHEMA = "javax.xml.accessExternalSchema"; + //Values for the ACCESS_EXTERNAL_* properties + public static final String ACCESS_EXTERNAL_ALL = "all"; + public static final String ACCESS_EXTERNAL_NONE = ""; + + // JDK 27 + public static final String SP_ACCESS = "jdk.xml.resource.access"; + // DTD/CATALOG constants public static final String RESOLVE_CONTINUE = "continue"; public static final String RESOLVE_IGNORE = "ignore"; public static final String RESOLVE_STRICT = "strict"; + public static final String RESOLVE_STRICT_NONLOCAL = "strict:non-local"; public static final String DTD_ALLOW = "allow"; public static final String DTD_IGNORE = "ignore"; public static final String DTD_DENY = "deny"; + // resource access constants + public static final String ACCESS_ALLOW = "*"; + public static final String ACCESS_DENY = ""; + // JAXP Configuration File(JCF) location // DTD = deny public static final String JCF_DTD2 = "dtd2.properties"; @@ -126,6 +142,8 @@ public class TestBase { public static final String CONFIG_DEFAULT = "jaxp.properties"; public static final String CONFIG_STRICT = "jaxp-strict.properties"; public static final String CONFIG_TEMPLATE_STRICT = "jaxp-strict.properties.template"; + public static final String CONFIG_COMPAT = "jaxp-compat.properties"; + public static final String CONFIG_TEMPLATE_COMPAT = "jaxp-compat.properties.template"; public static final String UNKNOWN_HOST = "invalid.site.com"; @@ -150,6 +168,16 @@ public class TestBase { CATALOG0(SP_CATALOG, "ditto", Type.PROPERTY, RESOLVE_CONTINUE), CATALOG1(SP_CATALOG, "ditto", Type.PROPERTY, RESOLVE_IGNORE), CATALOG2(SP_CATALOG, "ditto", Type.PROPERTY, RESOLVE_STRICT), + CATALOG_NONLOCAL(SP_CATALOG, "ditto", Type.PROPERTY, RESOLVE_STRICT_NONLOCAL), + AED0(XMLConstants.ACCESS_EXTERNAL_DTD, SP_ACCESS_EXTERNAL_DTD, Type.PROPERTY, ACCESS_EXTERNAL_ALL), + AED2(XMLConstants.ACCESS_EXTERNAL_DTD, SP_ACCESS_EXTERNAL_DTD, Type.PROPERTY, ACCESS_EXTERNAL_NONE), + AES0(XMLConstants.ACCESS_EXTERNAL_SCHEMA, SP_ACCESS_EXTERNAL_SCHEMA, Type.PROPERTY, ACCESS_EXTERNAL_ALL), + AES2(XMLConstants.ACCESS_EXTERNAL_SCHEMA, SP_ACCESS_EXTERNAL_SCHEMA, Type.PROPERTY, ACCESS_EXTERNAL_NONE), + AEX0(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, SP_ACCESS_EXTERNAL_STYLESHEET, Type.PROPERTY, ACCESS_EXTERNAL_ALL), + AEX2(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, SP_ACCESS_EXTERNAL_STYLESHEET, Type.PROPERTY, ACCESS_EXTERNAL_NONE), + + ACCESS0(SP_ACCESS, "ditto", Type.PROPERTY, ACCESS_ALLOW), + ACCESS1(SP_ACCESS, "ditto", Type.PROPERTY, ACCESS_DENY), // StAX properties SUPPORT_DTD(XMLInputFactory.SUPPORT_DTD, null, Type.FEATURE, "true"), @@ -243,10 +271,21 @@ public class TestBase { String error) throws Exception { //dbf.setAttribute(CatalogFeatures.Feature.RESOLVE.getPropertyName(), "continue"); DocumentBuilder builder = dbf.newDocumentBuilder(); + builder.setEntityResolver((String publicId, String systemId) -> { + InputSource dtd = null; + InputStream is = null; + if ("http://foo.bar/dtd/test.dtd".equals(systemId)) { + is = getClass().getResourceAsStream("/local/store/path/test.dtd"); + } + if (is != null) { + dtd = new InputSource(is); + } + return dtd; + }); File file = new File(getPath(TEST_SOURCE_DIR, filename)); try { - Document document = builder.parse(file); - Assert.assertTrue(!expectError); + builder.parse(file); + processResult(expectError, error); } catch (Exception e) { e.printStackTrace(); processError(expectError, error, e); @@ -259,7 +298,7 @@ public class TestBase { File file = new File(getPath(TEST_SOURCE_DIR, filename)); try { parser.parse(file, new DefaultHandler()); - Assert.assertTrue(!expectError); + processResult(expectError, error); } catch (Exception e) { //e.printStackTrace(); processError(expectError, error, e); @@ -275,8 +314,7 @@ public class TestBase { XMLStreamReader streamReader = xif.createXMLStreamReader(xml, entityxml); String text = getText(streamReader, XMLStreamConstants.CHARACTERS); System.out.println("Text: [" + text.trim() + "]"); - Assert.assertTrue(Pattern.matches(expected, text.trim())); - Assert.assertTrue(!expectError); + processResult(expectError, expected); } catch (Exception e) { e.printStackTrace(); processError(expectError, expected, e); @@ -289,8 +327,8 @@ public class TestBase { String xsd = getPath(TEST_SOURCE_DIR, filename); try { Schema schema = sf.newSchema(new StreamSource(new File(xsd))); - Assert.assertTrue(!expectError); - } catch (Exception e) { + processResult(expectError, expected); + } catch (SAXException e) { e.printStackTrace(); processError(expectError, expected, e); } @@ -303,8 +341,8 @@ public class TestBase { SAXSource xslSource = new SAXSource(new InputSource(xsl)); xslSource.setSystemId(xsl); Transformer transformer = tf.newTransformer(xslSource); - Assert.assertTrue(!expectError); - } catch (Exception e) { + processResult(expectError, expected); + } catch (TransformerConfigurationException e) { //e.printStackTrace(); processError(expectError, expected, e); } @@ -320,7 +358,7 @@ public class TestBase { Transformer transformer = tf.newTransformer(xslSource); StringWriter sw = new StringWriter(); transformer.transform(getSource(SourceType.STREAM, xmlSysId), new StreamResult(sw)); - Assert.assertTrue(!expectError); + processResult(expectError, expected); } catch (Exception e) { processError(expectError, expected, e); } @@ -333,16 +371,23 @@ public class TestBase { Schema schema = sf.newSchema(); Validator validator = schema.newValidator(); validator.validate(new StreamSource(new File(xml))); - Assert.assertTrue(!expectError); + processResult(expectError, expected); } catch (Exception e) { e.printStackTrace(); processError(expectError, expected, e); } } + protected void processResult(boolean expectError, String expected) { + if (expectError) { + Assert.assertTrue(false, "Expected error, but processing succeeded."); + } + } + protected void processError(boolean expectError, String error, Exception e) throws Exception { String str = e.getMessage(); + String errorText = getErrorText(e); if (!expectError) { Assert.assertTrue(false, "Expected pass, but Exception is thrown " + str); } else { @@ -351,11 +396,33 @@ public class TestBase { if (UNKNOWN_HOST.equals(error)) { Assert.assertTrue((str != null) && str.equals(error)); } else { - Assert.assertTrue((str != null) && str.contains(error)); + String matched = null; + for (String err : error.split(",")) { + String trimmed = err.trim(); + if (!trimmed.isEmpty() && errorText.contains(trimmed)) { + matched = trimmed; + break; + } + } + if (matched == null) { + Assert.assertTrue(false,"Missing expected error code(s): " + error); + } + Assert.assertTrue(true, "Found expected error code: " + matched); } } } + private String getErrorText(Throwable t) { + StringBuilder sb = new StringBuilder(); + while (t != null) { + if (t.getMessage() != null) { + sb.append(t.getMessage()).append('\n'); + } + t = t.getCause(); + } + return sb.toString(); + } + /** * Returns a DocumentBuilderFactory with settings as specified. * @@ -667,11 +734,15 @@ public class TestBase { System.setProperty(property.spName, property.value); break; case CONFIG_FILE: - System.setProperty(CONFIG_FILE, config.value); + if (config != null) { + System.setProperty(CONFIG_FILE, config.value); + } break; case CONFIG_FILE_SYSTEM: case CONFIG_FILE_SYSTEM_API: - System.setProperty(CONFIG_FILE, config.value); + if (config != null) { + System.setProperty(CONFIG_FILE, config.value); + } if (property != null) { System.setProperty(property.spName, property.value); } @@ -714,7 +785,7 @@ public class TestBase { } } - static String getPath(String base, String file) { + public static String getPath(String base, String file) { String temp = base + file; if (IS_WINDOWS) { temp = "/" + temp; @@ -734,6 +805,8 @@ public class TestBase { } else { throw new RuntimeException("Expected true but was false. "); } + } else if (message != null && !message.isEmpty()) { + System.out.println("Passed: " + message); } } diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/xmlfiles/XI_simple.xml b/test/jaxp/javax/xml/jaxp/unittest/common/xmlfiles/XI_simple.xml new file mode 100644 index 00000000000..98155ece7bb --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/xmlfiles/XI_simple.xml @@ -0,0 +1,2 @@ + +123