diff --git a/.hgtags b/.hgtags index 11724074994..a1a487be0aa 100644 --- a/.hgtags +++ b/.hgtags @@ -1029,3 +1029,7 @@ b81aa0cb626746f790f4b6fdcc71d84d00eff136 jdk8u332-b01 7376b980d6b085d5d9061d212f3ad69d239718e6 jdk8u332-b03 f58fc9077d2274b2832bc00ff16d112fe99d9ace jdk8u332-b04 2a92df021686242bb55ecd324b5dee00c45a0a8e jdk8u332-b05 +6d5c4e11830c154190021d6ae134c5f4162ff7cc jdk8u332-b06 +6d526dbc3432fd9f2db19bdcb2f6b5b8799d88f0 jdk8u332-b07 +95b31159fdfd496e521e119aba9ef54acf6b272e jdk8u332-b08 +37aca7715d13acfdc931aab7dbcdd41f9fd4b042 jdk8u332-b09 diff --git a/jaxp/src/com/sun/java_cup/internal/runtime/lr_parser.java b/jaxp/src/com/sun/java_cup/internal/runtime/lr_parser.java index a94dff88924..517adbf0b14 100644 --- a/jaxp/src/com/sun/java_cup/internal/runtime/lr_parser.java +++ b/jaxp/src/com/sun/java_cup/internal/runtime/lr_parser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2022, 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 com.sun.java_cup.internal.runtime; +import com.sun.org.apache.xalan.internal.xsltc.compiler.sym; +import java.util.Arrays; import java.util.Stack; /** This class implements a skeleton table driven LR parser. In general, @@ -134,9 +136,19 @@ * @see com.sun.java_cup.internal.runtime.Symbol * @see com.sun.java_cup.internal.runtime.virtual_parse_stack * @author Frank Flannery + * + * @LastModified: Jan 2022 */ public abstract class lr_parser { + public static final int ID_GROUP = 1; + public static final int ID_OPERATOR = 2; + public static final int ID_TOTAL_OPERATOR = 3; + + private boolean isLiteral = false; + private int grpCount = 0; + private int opCount = 0; + private int totalOpCount = 0; /*-----------------------------------------------------------*/ /*--- Constructor(s) ----------------------------------------*/ @@ -355,8 +367,34 @@ public void user_init() throws java.lang.Exception { } * the "scan with" clause. Do not recycle objects; every call to * scan() should return a fresh object. */ - public Symbol scan() throws java.lang.Exception { - return getScanner().next_token(); + public Symbol scan() throws Exception { + Symbol s = getScanner().next_token(); + + if (s.sym == sym.LPAREN) { + if (!isLiteral) { + grpCount++; + } + opCount++; // function + isLiteral = false; + } else if (contains(sym.OPERATORS, s.sym)) { + opCount++; + isLiteral = false; + } + + if (s.sym == sym.Literal || s.sym == sym.QNAME) { + isLiteral = true; + } + + return s; + } + + private boolean contains(final int[] arr, final int key) { + for (int i = 0 ; i < arr.length ; ++i) { + if (arr[i] == key) { + return true; + } + } + return false; } /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ @@ -552,6 +590,9 @@ public Symbol parse() throws java.lang.Exception /* do user initialization */ user_init(); + isLiteral = false; + grpCount = 0; + opCount = 0; /* get the first token */ cur_token = scan(); @@ -630,9 +671,29 @@ else if (act == 0) } } } + + totalOpCount += opCount; return lhs_sym; } + /** + * Returns the count of operators in XPath expressions. + * + * @param id the ID of the count + * @return the count associated with the ID + */ + public int getCount(int id) { + switch (id) { + case ID_GROUP: + return grpCount; + case ID_OPERATOR: + return opCount; + case ID_TOTAL_OPERATOR: + return totalOpCount; + } + return 0; + } + /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** Write a debugging message to System.err for the debugging version diff --git a/jaxp/src/com/sun/org/apache/xalan/internal/XalanConstants.java b/jaxp/src/com/sun/org/apache/xalan/internal/XalanConstants.java index 64514945258..f8374931789 100644 --- a/jaxp/src/com/sun/org/apache/xalan/internal/XalanConstants.java +++ b/jaxp/src/com/sun/org/apache/xalan/internal/XalanConstants.java @@ -175,6 +175,21 @@ public final class XalanConstants { */ public static final String JDK_EXTENSION_CLASSLOADER = "jdk.xml.transform.extensionClassLoader"; + /** + * JDK XPath Expression group limit + */ + public static final String XPATH_GROUP_LIMIT = "jdk.xml.xpathExprGrpLimit"; + + /** + * JDK XPath Expression operators limit + */ + public static final String XPATH_OP_LIMIT = "jdk.xml.xpathExprOpLimit"; + + /** + * JDK XSL XPath limit or Total Number of Operators Permitted in an XSL Stylesheet + */ + public static final String XPATH_TOTALOP_LIMIT = "jdk.xml.xpathTotalOpLimit"; + //legacy System Properties public final static String ENTITY_EXPANSION_LIMIT = "entityExpansionLimit"; public static final String ELEMENT_ATTRIBUTE_LIMIT = "elementAttributeLimit" ; diff --git a/jaxp/src/com/sun/org/apache/xalan/internal/xsltc/compiler/Parser.java b/jaxp/src/com/sun/org/apache/xalan/internal/xsltc/compiler/Parser.java index 08ec875bdb8..342183a9301 100644 --- a/jaxp/src/com/sun/org/apache/xalan/internal/xsltc/compiler/Parser.java +++ b/jaxp/src/com/sun/org/apache/xalan/internal/xsltc/compiler/Parser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -24,7 +24,7 @@ import com.sun.org.apache.xalan.internal.XalanConstants; import com.sun.org.apache.xalan.internal.utils.ObjectFactory; import com.sun.org.apache.xalan.internal.utils.SecuritySupport; -import com.sun.org.apache.xalan.internal.utils.XMLSecurityManager; +import jdk.xml.internal.XMLSecurityManager; import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg; import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodType; import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type; @@ -465,8 +465,10 @@ public SyntaxTreeNode parse(InputSource input) { XMLSecurityManager securityManager = (XMLSecurityManager) _xsltc.getProperty(XalanConstants.SECURITY_MANAGER); for (XMLSecurityManager.Limit limit : XMLSecurityManager.Limit.values()) { - lastProperty = limit.apiProperty(); - reader.setProperty(lastProperty, securityManager.getLimitValueAsString(limit)); + if (limit.isSupported(XMLSecurityManager.Processor.PARSER)) { + lastProperty = limit.apiProperty(); + reader.setProperty(lastProperty, securityManager.getLimitValueAsString(limit)); + } } if (securityManager.printEntityCountInfo()) { lastProperty = XalanConstants.JDK_ENTITY_COUNT_INFO; @@ -1121,6 +1123,9 @@ private SyntaxTreeNode parseTopLevel(SyntaxTreeNode parent, String text, expression, parent)); } catch (Exception e) { + if (ErrorMsg.XPATH_LIMIT.equals(e.getMessage())) { + throw new RuntimeException(ErrorMsg.XPATH_LIMIT); + } if (_xsltc.debug()) e.printStackTrace(); reportError(ERROR, new ErrorMsg(ErrorMsg.XPATH_PARSER_ERR, expression, parent)); diff --git a/jaxp/src/com/sun/org/apache/xalan/internal/xsltc/compiler/XPathParser.java b/jaxp/src/com/sun/org/apache/xalan/internal/xsltc/compiler/XPathParser.java index 8a4fba5b49a..dc54a7ba332 100644 --- a/jaxp/src/com/sun/org/apache/xalan/internal/xsltc/compiler/XPathParser.java +++ b/jaxp/src/com/sun/org/apache/xalan/internal/xsltc/compiler/XPathParser.java @@ -9,6 +9,10 @@ import java.util.Stack; import java.util.Vector; import java.io.StringReader; +import com.sun.org.apache.xalan.internal.XalanConstants; +import jdk.xml.internal.XMLLimitAnalyzer; +import jdk.xml.internal.XMLSecurityManager; +import jdk.xml.internal.XMLSecurityManager.Limit; import com.sun.java_cup.internal.runtime.*; import com.sun.org.apache.xml.internal.dtm.DTM; import com.sun.org.apache.xalan.internal.xsltc.DOM; @@ -20,9 +24,12 @@ * CUP v0.11b generated parser. * This class was generated by CUP v0.11b on Nov 12, 2019. * - * @LastModified: Nov 2019 + * @LastModified: Jan 2022 */ public class XPathParser extends com.sun.java_cup.internal.runtime.lr_parser { + private int grpLimit = 0; + private int opLimit = 0; + private int totalOpLimit = 0; /** Default constructor. */ public XPathParser() { @@ -929,10 +936,19 @@ public int error_sym() { */ public SymbolTable _symbolTable; + private XMLSecurityManager _xmlSM; + private XMLLimitAnalyzer _limitAnalyzer = null; + public XPathParser(Parser parser) { _parser = parser; _xsltc = parser.getXSLTC(); _symbolTable = parser.getSymbolTable(); + _xmlSM = (XMLSecurityManager)_xsltc.getProperty(XalanConstants.SECURITY_MANAGER); + _limitAnalyzer = new XMLLimitAnalyzer(); + // no limits if _xmlSM is null + grpLimit = (_xmlSM != null) ? _xmlSM.getLimit(Limit.XPATH_GROUP_LIMIT) : 0; + opLimit = (_xmlSM != null) ? _xmlSM.getLimit(Limit.XPATH_OP_LIMIT) : 0; + totalOpLimit = (_xmlSM != null) ? _xmlSM.getLimit(Limit.XPATH_TOTALOP_LIMIT) : 0; } public int getLineNumber() { @@ -1078,7 +1094,32 @@ public Symbol parse(String expression, int lineNumber) throws Exception { try { _expression = expression; _lineNumber = lineNumber; - return super.parse(); + Symbol s = super.parse(); + int grpCount = getCount(ID_GROUP); + int opCount = getCount(ID_OPERATOR); + int totalOpCount = getCount(ID_TOTAL_OPERATOR); + + String errCode = null; + Object[] params = null; + if (grpLimit > 0 && grpCount > grpLimit) { + errCode = ErrorMsg.XPATH_GROUP_LIMIT; + params = new Object[]{grpCount, grpLimit, + _xmlSM.getStateLiteral(Limit.XPATH_GROUP_LIMIT)}; + } else if (opLimit > 0 && opCount > opLimit) { + errCode = ErrorMsg.XPATH_OPERATOR_LIMIT; + params = new Object[]{opCount, opLimit, + _xmlSM.getStateLiteral(Limit.XPATH_OP_LIMIT)}; + } else if (totalOpLimit > 0 && totalOpCount > totalOpLimit) { + errCode = ErrorMsg.XPATH_TOTAL_OPERATOR_LIMIT; + params = new Object[]{totalOpCount, totalOpLimit, + _xmlSM.getStateLiteral(Limit.XPATH_TOTALOP_LIMIT)}; + } + if (errCode != null) { + _parser.reportError(Constants.FATAL, + new ErrorMsg(errCode, lineNumber, params)); + throw new RuntimeException(ErrorMsg.XPATH_LIMIT); + } + return s; } catch (IllegalCharException e) { ErrorMsg err = new ErrorMsg(ErrorMsg.ILLEGAL_CHAR_ERR, lineNumber, e.getMessage()); diff --git a/jaxp/src/com/sun/org/apache/xalan/internal/xsltc/compiler/XSLTC.java b/jaxp/src/com/sun/org/apache/xalan/internal/xsltc/compiler/XSLTC.java index 8b4225eda6a..c49b0432ab4 100644 --- a/jaxp/src/com/sun/org/apache/xalan/internal/xsltc/compiler/XSLTC.java +++ b/jaxp/src/com/sun/org/apache/xalan/internal/xsltc/compiler/XSLTC.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -23,7 +23,6 @@ import com.sun.org.apache.bcel.internal.classfile.JavaClass; import com.sun.org.apache.xalan.internal.XalanConstants; import com.sun.org.apache.xalan.internal.utils.SecuritySupport; -import com.sun.org.apache.xalan.internal.utils.XMLSecurityManager; import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg; import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util; import com.sun.org.apache.xml.internal.dtm.DTM; @@ -47,6 +46,7 @@ import java.util.jar.Manifest; import javax.xml.XMLConstants; import jdk.xml.internal.JdkXmlFeatures; +import jdk.xml.internal.XMLSecurityManager; import org.xml.sax.InputSource; import org.xml.sax.XMLReader; @@ -481,7 +481,10 @@ else if (systemId != null && !systemId.equals("")) { } } catch (Exception e) { - /*if (_debug)*/ e.printStackTrace(); + if (_debug) e.printStackTrace(); + if (ErrorMsg.XPATH_LIMIT.equals(e.getMessage())) { + return !_parser.errorsFound(); + } _parser.reportError(Constants.FATAL, new ErrorMsg(ErrorMsg.JAXP_COMPILE_ERR, e)); } catch (Error e) { diff --git a/jaxp/src/com/sun/org/apache/xalan/internal/xsltc/compiler/sym.java b/jaxp/src/com/sun/org/apache/xalan/internal/xsltc/compiler/sym.java index d64dc3fd046..88244a31ab0 100644 --- a/jaxp/src/com/sun/org/apache/xalan/internal/xsltc/compiler/sym.java +++ b/jaxp/src/com/sun/org/apache/xalan/internal/xsltc/compiler/sym.java @@ -6,6 +6,8 @@ package com.sun.org.apache.xalan.internal.xsltc.compiler; +import java.util.Arrays; + /** CUP generated class containing symbol constants. */ public class sym { /* terminals */ @@ -63,4 +65,12 @@ public class sym { public static final int ATTRIBUTE = 41; public static final int GT = 19; public static final int NODE = 31; + /* + AXES: count once at DCOLON, + these axes names are therefore not counted: + NAMESPACE, FOLLOWINGSIBLING, CHILD, DESCENDANTORSELF, DESCENDANT + , PRECEDINGSIBLING, SELF, ANCESTORORSELF, PRECEDING, ANCESTOROR, PARENT, FOLLOWING, ATTRIBUTE + */ + public static final int[] OPERATORS = {GE, SLASH, ATSIGN, LPAREN, DCOLON, + MINUS, STAR, LT, OR, DIV, PLUS, LE, VBAR, MOD, EQ, LBRACK, DOLLAR, NE, GT}; } diff --git a/jaxp/src/com/sun/org/apache/xalan/internal/xsltc/compiler/util/ErrorMessages.java b/jaxp/src/com/sun/org/apache/xalan/internal/xsltc/compiler/util/ErrorMessages.java index 52f944101dc..8854a01ea0a 100644 --- a/jaxp/src/com/sun/org/apache/xalan/internal/xsltc/compiler/util/ErrorMessages.java +++ b/jaxp/src/com/sun/org/apache/xalan/internal/xsltc/compiler/util/ErrorMessages.java @@ -24,6 +24,7 @@ /** * @author Morten Jorgensen + * @LastModified: Jan 2022 */ public class ErrorMessages extends ListResourceBundle { @@ -1011,12 +1012,22 @@ public Object[][] getContents() "smaller templates." }, - {ErrorMsg.DESERIALIZE_TRANSLET_ERR, "When Java security is enabled, " + - "support for deserializing TemplatesImpl is disabled." + - "This can be overridden by setting the jdk.xml.enableTemplatesImplDeserialization" + - " system property to true."} - - }; + {ErrorMsg.DESERIALIZE_TRANSLET_ERR, "When Java security is enabled, " + + "support for deserializing TemplatesImpl is disabled. This can be " + + "overridden by setting the jdk.xml.enableTemplatesImplDeserialization" + + " system property to true."}, + + {ErrorMsg.XPATH_GROUP_LIMIT, + "JAXP0801001: the compiler encountered an XPath expression containing " + + "''{0}'' groups that exceeds the ''{1}'' limit set by ''{2}''."}, + + {ErrorMsg.XPATH_OPERATOR_LIMIT, + "JAXP0801002: the compiler encountered an XPath expression containing " + + "''{0}'' operators that exceeds the ''{1}'' limit set by ''{2}''."}, + {ErrorMsg.XPATH_TOTAL_OPERATOR_LIMIT, + "JAXP0801003: the compiler encountered XPath expressions with an accumulated " + + "''{0}'' operators that exceeds the ''{1}'' limit set by ''{2}''."}, + }; } } diff --git a/jaxp/src/com/sun/org/apache/xalan/internal/xsltc/compiler/util/ErrorMsg.java b/jaxp/src/com/sun/org/apache/xalan/internal/xsltc/compiler/util/ErrorMsg.java index 4192cf2d7e6..365f6c747e5 100644 --- a/jaxp/src/com/sun/org/apache/xalan/internal/xsltc/compiler/util/ErrorMsg.java +++ b/jaxp/src/com/sun/org/apache/xalan/internal/xsltc/compiler/util/ErrorMsg.java @@ -172,6 +172,11 @@ public final class ErrorMsg { public static final String DESERIALIZE_TRANSLET_ERR = "DESERIALIZE_TEMPLATES_ERR"; + public static final String XPATH_LIMIT = "XPATH_LIMIT"; + public static final String XPATH_GROUP_LIMIT = "XPATH_GROUP_LIMIT"; + public static final String XPATH_OPERATOR_LIMIT = "XPATH_OPERATOR_LIMIT"; + public static final String XPATH_TOTAL_OPERATOR_LIMIT = "XPATH_TOTAL_OPERATOR_LIMIT"; + // All error messages are localized and are stored in resource bundles. // This array and the following 4 strings are read from that bundle. private static ResourceBundle _bundle; @@ -208,7 +213,11 @@ public ErrorMsg(String message, int line) { public ErrorMsg(String code, int line, Object param) { _code = code; _line = line; - _params = new Object[] { param }; + if (param instanceof Object[]) { + _params = (Object[])param; + } else { + _params = new Object[] { param }; + } } public ErrorMsg(String code, Object param) { diff --git a/jaxp/src/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerFactoryImpl.java b/jaxp/src/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerFactoryImpl.java index 549a3285eaa..cb596ac71cc 100644 --- a/jaxp/src/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerFactoryImpl.java +++ b/jaxp/src/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerFactoryImpl.java @@ -24,7 +24,7 @@ import com.sun.org.apache.xalan.internal.utils.FeaturePropertyBase.State; import com.sun.org.apache.xalan.internal.utils.ObjectFactory; import com.sun.org.apache.xalan.internal.utils.SecuritySupport; -import com.sun.org.apache.xalan.internal.utils.XMLSecurityManager; +import jdk.xml.internal.XMLSecurityManager; import com.sun.org.apache.xalan.internal.utils.XMLSecurityPropertyManager; import com.sun.org.apache.xalan.internal.utils.XMLSecurityPropertyManager.Property; import com.sun.org.apache.xalan.internal.xsltc.compiler.Constants; diff --git a/jaxp/src/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerImpl.java b/jaxp/src/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerImpl.java index 3bd46e17dd2..8e783cee0b5 100644 --- a/jaxp/src/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerImpl.java +++ b/jaxp/src/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2022, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -24,7 +24,6 @@ package com.sun.org.apache.xalan.internal.xsltc.trax; import com.sun.org.apache.xalan.internal.XalanConstants; -import com.sun.org.apache.xalan.internal.utils.XMLSecurityManager; import com.sun.org.apache.xalan.internal.utils.SecuritySupport; import com.sun.org.apache.xalan.internal.xsltc.DOM; import com.sun.org.apache.xalan.internal.xsltc.DOMCache; @@ -80,6 +79,7 @@ import javax.xml.transform.stax.StAXSource; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; +import jdk.xml.internal.XMLSecurityManager; import jdk.xml.internal.JdkXmlUtils; import org.xml.sax.ContentHandler; import org.xml.sax.InputSource; diff --git a/jaxp/src/com/sun/org/apache/xalan/internal/xsltc/trax/Util.java b/jaxp/src/com/sun/org/apache/xalan/internal/xsltc/trax/Util.java index 91d8491798e..a54208573fc 100644 --- a/jaxp/src/com/sun/org/apache/xalan/internal/xsltc/trax/Util.java +++ b/jaxp/src/com/sun/org/apache/xalan/internal/xsltc/trax/Util.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved. */ /* * Copyright 2001-2004 The Apache Software Foundation. @@ -39,7 +39,7 @@ import javax.xml.transform.stream.StreamSource; import jdk.xml.internal.JdkXmlFeatures; import jdk.xml.internal.JdkXmlUtils; -import com.sun.org.apache.xalan.internal.utils.XMLSecurityManager; +import jdk.xml.internal.XMLSecurityManager; import com.sun.org.apache.xalan.internal.xsltc.compiler.XSLTC; import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg; @@ -118,9 +118,11 @@ public static InputSource getInputSource(XSLTC xsltc, Source source) (XMLSecurityManager)xsltc.getProperty(XalanConstants.SECURITY_MANAGER); if (securityManager != null) { for (XMLSecurityManager.Limit limit : XMLSecurityManager.Limit.values()) { - lastProperty = limit.apiProperty(); - reader.setProperty(lastProperty, - securityManager.getLimitValueAsString(limit)); + if (limit.isSupported(XMLSecurityManager.Processor.PARSER)) { + lastProperty = limit.apiProperty(); + reader.setProperty(lastProperty, + securityManager.getLimitValueAsString(limit)); + } } if (securityManager.printEntityCountInfo()) { lastProperty = XalanConstants.JDK_ENTITY_COUNT_INFO; diff --git a/jaxp/src/com/sun/org/apache/xerces/internal/parsers/AbstractDOMParser.java b/jaxp/src/com/sun/org/apache/xerces/internal/parsers/AbstractDOMParser.java index 68c3acd3fbb..cac955dd8d8 100644 --- a/jaxp/src/com/sun/org/apache/xerces/internal/parsers/AbstractDOMParser.java +++ b/jaxp/src/com/sun/org/apache/xerces/internal/parsers/AbstractDOMParser.java @@ -58,6 +58,7 @@ import com.sun.org.apache.xerces.internal.xs.ElementPSVI; import com.sun.org.apache.xerces.internal.xs.XSTypeDefinition; import com.sun.org.apache.xerces.internal.utils.ObjectFactory; +import jdk.xml.internal.JdkXmlUtils; import org.w3c.dom.Attr; import org.w3c.dom.CDATASection; import org.w3c.dom.Comment; @@ -2027,17 +2028,8 @@ public void externalEntityDecl (String name, XMLResourceIdentifier identifier, else { fInternalSubset.append (name); } - fInternalSubset.append (' '); - if (publicId != null) { - fInternalSubset.append ("PUBLIC '"); - fInternalSubset.append (publicId); - fInternalSubset.append ("' '"); - } - else { - fInternalSubset.append ("SYSTEM '"); - } - fInternalSubset.append (literalSystemId); - fInternalSubset.append ("'>\n"); + fInternalSubset.append (JdkXmlUtils.getDTDExternalDecl(publicId, literalSystemId)); + fInternalSubset.append (">\n"); } // NOTE: We only know how to create these nodes for the Xerces @@ -2167,20 +2159,8 @@ public void unparsedEntityDecl (String name, XMLResourceIdentifier identifier, if (fInternalSubset != null && !fInDTDExternalSubset) { fInternalSubset.append ("\n"); } @@ -2247,19 +2227,8 @@ public void notationDecl (String name, XMLResourceIdentifier identifier, if (fInternalSubset != null && !fInDTDExternalSubset) { fInternalSubset.append ("\n"); + fInternalSubset.append (JdkXmlUtils.getDTDExternalDecl(publicId, literalSystemId)); + fInternalSubset.append (">\n"); } // NOTE: We only know how to create these nodes for the Xerces diff --git a/jaxp/src/com/sun/org/apache/xml/internal/serializer/ToHTMLStream.java b/jaxp/src/com/sun/org/apache/xml/internal/serializer/ToHTMLStream.java index b3e1fa3d238..255bb04e22e 100644 --- a/jaxp/src/com/sun/org/apache/xml/internal/serializer/ToHTMLStream.java +++ b/jaxp/src/com/sun/org/apache/xml/internal/serializer/ToHTMLStream.java @@ -32,6 +32,7 @@ import com.sun.org.apache.xml.internal.serializer.utils.MsgKey; import com.sun.org.apache.xml.internal.serializer.utils.Utils; +import jdk.xml.internal.JdkXmlUtils; import org.xml.sax.Attributes; import org.xml.sax.SAXException; @@ -44,7 +45,7 @@ * because it is used from another package. * * @xsl.usage internal - * @LastModified: Sept 2018 + * @LastModified: July 2021 */ public final class ToHTMLStream extends ToStream { @@ -680,28 +681,10 @@ protected void startDocumentInternal() throws org.xml.sax.SAXException final java.io.Writer writer = m_writer; try { - writer.write("'); - outputLineSep(); + writer.write("'); + outputLineSep(); } catch(IOException e) { diff --git a/jaxp/src/com/sun/org/apache/xml/internal/serializer/ToStream.java b/jaxp/src/com/sun/org/apache/xml/internal/serializer/ToStream.java index cac28362d0b..76a3c9700f7 100644 --- a/jaxp/src/com/sun/org/apache/xml/internal/serializer/ToStream.java +++ b/jaxp/src/com/sun/org/apache/xml/internal/serializer/ToStream.java @@ -57,7 +57,7 @@ * serializers (xml, html, text ...) that write output to a stream. * * @xsl.usage internal - * @LastModified: Apr 2021 + * @LastModified: July 2021 */ abstract public class ToStream extends SerializerBase { @@ -953,16 +953,8 @@ public void externalEntityDecl( m_writer.write(""); + m_writer.write(JdkXmlUtils.getDTDExternalDecl(publicId, systemId)); + m_writer.write(">"); m_writer.write(m_lineSep, 0, m_lineSepLen); } catch (IOException e) { // TODO Auto-generated catch block @@ -1936,27 +1928,11 @@ void outputDocTypeDecl(String name, boolean closeDecl) throws SAXException final java.io.Writer writer = m_writer; writer.write(""); @@ -1964,17 +1940,6 @@ void outputDocTypeDecl(String name, boolean closeDecl) throws SAXException closeDecl = false; // done closing } } - boolean dothis = false; - if (dothis) - { - // at one point this code seemed right, - // but not anymore - Brian M. - if (closeDecl) - { - writer.write('>'); - writer.write(m_lineSep, 0, m_lineSepLen); - } - } } catch (IOException e) { @@ -3373,16 +3338,8 @@ public void notationDecl(String name, String pubID, String sysID) throws SAXExce m_writer.write(""); + m_writer.write(JdkXmlUtils.getDTDExternalDecl(pubID, sysID)); + m_writer.write(">"); m_writer.write(m_lineSep, 0, m_lineSepLen); } catch (IOException e) { // TODO Auto-generated catch block @@ -3403,16 +3360,8 @@ public void unparsedEntityDecl(String name, String pubID, String sysID, String n m_writer.write(""); m_writer.write(m_lineSep, 0, m_lineSepLen); diff --git a/jaxp/src/com/sun/org/apache/xml/internal/utils/XMLReaderManager.java b/jaxp/src/com/sun/org/apache/xml/internal/utils/XMLReaderManager.java index 39a5e88981a..71a9cf14d63 100644 --- a/jaxp/src/com/sun/org/apache/xml/internal/utils/XMLReaderManager.java +++ b/jaxp/src/com/sun/org/apache/xml/internal/utils/XMLReaderManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2022, Oracle and/or its affiliates. All rights reserved. */ /* * Copyright 1999-2004 The Apache Software Foundation. @@ -23,11 +23,11 @@ import com.sun.org.apache.xalan.internal.XalanConstants; import com.sun.org.apache.xalan.internal.utils.SecuritySupport; -import com.sun.org.apache.xalan.internal.utils.XMLSecurityManager; import java.util.HashMap; import javax.xml.XMLConstants; import jdk.xml.internal.JdkXmlUtils; +import jdk.xml.internal.XMLSecurityManager; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; @@ -133,9 +133,11 @@ public synchronized XMLReader getXMLReader() throws SAXException { try { if (_xmlSecurityManager != null) { for (XMLSecurityManager.Limit limit : XMLSecurityManager.Limit.values()) { - lastProperty = limit.apiProperty(); - reader.setProperty(lastProperty, - _xmlSecurityManager.getLimitValueAsString(limit)); + if (limit.isSupported(XMLSecurityManager.Processor.PARSER)) { + lastProperty = limit.apiProperty(); + reader.setProperty(lastProperty, + _xmlSecurityManager.getLimitValueAsString(limit)); + } } if (_xmlSecurityManager.printEntityCountInfo()) { lastProperty = XalanConstants.JDK_ENTITY_COUNT_INFO; diff --git a/jaxp/src/com/sun/org/apache/xpath/internal/XPath.java b/jaxp/src/com/sun/org/apache/xpath/internal/XPath.java index 699d464ef0e..2bdfca5e178 100644 --- a/jaxp/src/com/sun/org/apache/xpath/internal/XPath.java +++ b/jaxp/src/com/sun/org/apache/xpath/internal/XPath.java @@ -28,6 +28,7 @@ import com.sun.org.apache.xalan.internal.res.XSLMessages; import com.sun.org.apache.xml.internal.dtm.DTM; +import com.sun.org.apache.xml.internal.utils.DefaultErrorHandler; import com.sun.org.apache.xml.internal.utils.PrefixResolver; import com.sun.org.apache.xml.internal.utils.SAXSourceLocator; import com.sun.org.apache.xpath.internal.compiler.Compiler; @@ -36,6 +37,7 @@ import com.sun.org.apache.xpath.internal.functions.Function; import com.sun.org.apache.xpath.internal.objects.XObject; import com.sun.org.apache.xpath.internal.res.XPATHErrorResources; +import jdk.xml.internal.XMLSecurityManager; /** * The XPath class wraps an expression object and provides general services @@ -160,40 +162,11 @@ public String getPatternString() * * @throws javax.xml.transform.TransformerException if syntax or other error. */ - public XPath( - String exprString, SourceLocator locator, PrefixResolver prefixResolver, int type, - ErrorListener errorListener) - throws javax.xml.transform.TransformerException + public XPath(String exprString, SourceLocator locator, PrefixResolver prefixResolver, + int type, ErrorListener errorListener) + throws TransformerException { - initFunctionTable(); - if(null == errorListener) - errorListener = new com.sun.org.apache.xml.internal.utils.DefaultErrorHandler(); - - m_patternString = exprString; - - XPathParser parser = new XPathParser(errorListener, locator); - Compiler compiler = new Compiler(errorListener, locator, m_funcTable); - - if (SELECT == type) - parser.initXPath(compiler, exprString, prefixResolver); - else if (MATCH == type) - parser.initMatchPattern(compiler, exprString, prefixResolver); - else - throw new RuntimeException(XSLMessages.createXPATHMessage( - XPATHErrorResources.ER_CANNOT_DEAL_XPATH_TYPE, - new Object[]{Integer.toString(type)})); - - // System.out.println("----------------"); - Expression expr = compiler.compileExpression(0); - - // System.out.println("expr: "+expr); - this.setExpression(expr); - - if((null != locator) && locator instanceof ExpressionNode) - { - expr.exprSetParent((ExpressionNode)locator); - } - + this(exprString, locator, prefixResolver, type, errorListener, null); } /** @@ -207,22 +180,27 @@ else if (MATCH == type) * namespace URIs. * @param type one of {@link #SELECT} or {@link #MATCH}. * @param errorListener The error listener, or null if default should be used. + * @param funcTable the function table + * @param xmlSecMgr the XML security manager * * @throws javax.xml.transform.TransformerException if syntax or other error. */ - public XPath( - String exprString, SourceLocator locator, - PrefixResolver prefixResolver, int type, - ErrorListener errorListener, FunctionTable aTable) - throws javax.xml.transform.TransformerException + public XPath(String exprString, SourceLocator locator, PrefixResolver prefixResolver, + int type, ErrorListener errorListener, FunctionTable funcTable, + XMLSecurityManager xmlSecMgr) + throws TransformerException { - m_funcTable = aTable; + if (funcTable == null) { + initFunctionTable(); + } else { + m_funcTable = funcTable; + } if(null == errorListener) - errorListener = new com.sun.org.apache.xml.internal.utils.DefaultErrorHandler(); + errorListener = new DefaultErrorHandler(); m_patternString = exprString; - XPathParser parser = new XPathParser(errorListener, locator); + XPathParser parser = new XPathParser(errorListener, locator, xmlSecMgr); Compiler compiler = new Compiler(errorListener, locator, m_funcTable); if (SELECT == type) @@ -261,13 +239,32 @@ else if (MATCH == type) * * @throws javax.xml.transform.TransformerException if syntax or other error. */ - public XPath( - String exprString, SourceLocator locator, PrefixResolver prefixResolver, int type) - throws javax.xml.transform.TransformerException + public XPath(String exprString, SourceLocator locator, PrefixResolver prefixResolver, + int type) + throws TransformerException { this(exprString, locator, prefixResolver, type, null); } + /** + * Constructs an XPath object. + * + * @param exprString The XPath expression. + * @param locator The location of the expression, may be null. + * @param prefixResolver A prefix resolver to use to resolve prefixes to + * namespace URIs. + * @param type one of {@link #SELECT} or {@link #MATCH}. + * @param errorListener The error listener, or null if default should be used. + * @param funcTable the function table + * @throws TransformerException + */ + public XPath(String exprString, SourceLocator locator, PrefixResolver prefixResolver, + int type, ErrorListener errorListener, FunctionTable funcTable) + throws TransformerException + { + this(exprString, locator, prefixResolver, type, errorListener, funcTable, null); + } + /** * Construct an XPath object. * diff --git a/jaxp/src/com/sun/org/apache/xpath/internal/compiler/Lexer.java b/jaxp/src/com/sun/org/apache/xpath/internal/compiler/Lexer.java index b7206ca5155..c3d57a88ba4 100644 --- a/jaxp/src/com/sun/org/apache/xpath/internal/compiler/Lexer.java +++ b/jaxp/src/com/sun/org/apache/xpath/internal/compiler/Lexer.java @@ -23,8 +23,12 @@ import java.util.Vector; +import com.sun.org.apache.xalan.internal.res.XSLMessages; import com.sun.org.apache.xml.internal.utils.PrefixResolver; import com.sun.org.apache.xpath.internal.res.XPATHErrorResources; +import javax.xml.transform.TransformerException; +import jdk.xml.internal.XMLSecurityManager; +import jdk.xml.internal.XMLSecurityManager.Limit; /** * This class is in charge of lexical processing of the XPath @@ -70,6 +74,24 @@ class Lexer */ private int m_patternMapSize; + // XML security manager + XMLSecurityManager m_xmlSecMgr; + + // operator limit + private int m_opCountLimit; + + // group limit + private int m_grpCountLimit; + + // count of operators + private int m_opCount; + + // count of groups + private int m_grpCount; + + // indicate whether the current token is a literal + private boolean isLiteral = false; + /** * Create a Lexer object. * @@ -77,14 +99,22 @@ class Lexer * @param resolver The prefix resolver for mapping qualified name prefixes * to namespace URIs. * @param xpathProcessor The parser that is processing strings to opcodes. + * @param xmlSecMgr the XML security manager */ Lexer(Compiler compiler, PrefixResolver resolver, - XPathParser xpathProcessor) + XPathParser xpathProcessor, XMLSecurityManager xmlSecMgr) { - m_compiler = compiler; m_namespaceContext = resolver; m_processor = xpathProcessor; + m_xmlSecMgr = xmlSecMgr; + /** + * No limits if XML Security Manager is null. Applications using XPath through + * the public API always have a XMLSecurityManager. Applications invoking + * the internal XPath API shall consider using the public API instead. + */ + m_opCountLimit = (xmlSecMgr != null) ? xmlSecMgr.getLimit(Limit.XPATH_OP_LIMIT) : 0; + m_grpCountLimit = (xmlSecMgr != null) ? xmlSecMgr.getLimit(Limit.XPATH_GROUP_LIMIT) : 0; } /** @@ -135,7 +165,7 @@ void tokenize(String pat, Vector targetStrings) switch (c) { - case '\"' : + case Token.DQ : { if (startSubstring != -1) { @@ -170,7 +200,7 @@ void tokenize(String pat, Vector targetStrings) } } break; - case '\'' : + case Token.SQ : if (startSubstring != -1) { isNum = false; @@ -189,9 +219,9 @@ void tokenize(String pat, Vector targetStrings) startSubstring = i; - for (i++; (i < nChars) && ((c = pat.charAt(i)) != '\''); i++); + for (i++; (i < nChars) && ((c = pat.charAt(i)) != Token.SQ); i++); - if (c == '\'' && i < nChars) + if (c == Token.SQ && i < nChars) { addToTokenQueue(pat.substring(startSubstring, i + 1)); @@ -219,18 +249,24 @@ void tokenize(String pat, Vector targetStrings) } else { - addToTokenQueue(pat.substring(startSubstring, i)); + // check operator symbol + String s = pat.substring(startSubstring, i); + if (Token.contains(s)) { + m_opCount++; + isLiteral = false; + } + addToTokenQueue(s); } startSubstring = -1; } break; - case '@' : + case Token.AT : isAttrName = true; // fall-through on purpose - case '-' : - if ('-' == c) + case Token.MINUS : + if (Token.MINUS == c) { if (!(isNum || (startSubstring == -1))) { @@ -241,22 +277,22 @@ void tokenize(String pat, Vector targetStrings) } // fall-through on purpose - case '(' : - case '[' : - case ')' : - case ']' : - case '|' : - case '/' : - case '*' : - case '+' : - case '=' : - case ',' : + case Token.LPAREN : + case Token.LBRACK : + case Token.RPAREN : + case Token.RBRACK : + case Token.VBAR : + case Token.SLASH : + case Token.STAR : + case Token.PLUS : + case Token.EQ : + case Token.COMMA : case '\\' : // Unused at the moment case '^' : // Unused at the moment - case '!' : // Unused at the moment - case '$' : - case '<' : - case '>' : + case Token.EM : // Unused at the moment + case Token.DOLLAR : + case Token.LT : + case Token.GT : if (startSubstring != -1) { isNum = false; @@ -274,11 +310,11 @@ void tokenize(String pat, Vector targetStrings) startSubstring = -1; } - else if (('/' == c) && isStartOfPat) + else if ((Token.SLASH == c) && isStartOfPat) { isStartOfPat = mapPatternElemPos(nesting, isStartOfPat, isAttrName); } - else if ('*' == c) + else if (Token.STAR == c) { isStartOfPat = mapPatternElemPos(nesting, isStartOfPat, isAttrName); isAttrName = false; @@ -286,7 +322,7 @@ else if ('*' == c) if (0 == nesting) { - if ('|' == c) + if (Token.VBAR == c) { if (null != targetStrings) { @@ -297,18 +333,32 @@ else if ('*' == c) } } - if ((')' == c) || (']' == c)) + if ((Token.RPAREN == c) || (Token.RBRACK == c)) { nesting--; } - else if (('(' == c) || ('[' == c)) + else if ((Token.LPAREN == c) || (Token.LBRACK == c)) { nesting++; + if (!isLiteral && (Token.LPAREN == c)) { + m_grpCount++; + m_opCount++; + isLiteral = false; + } + } + + if ((Token.GT == c || Token.LT == c || Token.EQ == c) && Token.EQ != peekNext(pat, i)) { + m_opCount++; + isLiteral = false; + } + else if ((Token.LPAREN != c) && (Token.RPAREN != c) && (Token.RBRACK != c)) { + m_opCount++; + isLiteral = false; } addToTokenQueue(pat.substring(i, i + 1)); break; - case ':' : + case Token.COLON_CHAR: if (i>0) { if (posOfNSSep == (i - 1)) @@ -323,7 +373,7 @@ else if (('(' == c) || ('[' == c)) isAttrName = false; startSubstring = -1; posOfNSSep = -1; - + m_opCount++; addToTokenQueue(pat.substring(i - 1, i + 1)); break; @@ -336,6 +386,7 @@ else if (('(' == c) || ('[' == c)) // fall through on purpose default : + isLiteral = true; if (-1 == startSubstring) { startSubstring = i; @@ -346,6 +397,20 @@ else if (isNum) isNum = Character.isDigit(c); } } + if (m_grpCountLimit > 0 && m_grpCount > m_grpCountLimit) { + throw new TransformerException(XSLMessages.createXPATHMessage( + XPATHErrorResources.ER_XPATH_GROUP_LIMIT, + new Object[]{Integer.toString(m_grpCount), + Integer.toString(m_grpCountLimit), + m_xmlSecMgr.getStateLiteral(Limit.XPATH_GROUP_LIMIT)})); + } + if (m_opCountLimit > 0 && m_opCount > m_opCountLimit) { + throw new TransformerException(XSLMessages.createXPATHMessage( + XPATHErrorResources.ER_XPATH_OPERATOR_LIMIT, + new Object[]{Integer.toString(m_opCount), + Integer.toString(m_opCountLimit), + m_xmlSecMgr.getStateLiteral(Limit.XPATH_OP_LIMIT)})); + } } if (startSubstring != -1) @@ -376,6 +441,19 @@ else if (null != targetStrings) m_processor.m_queueMark = 0; } + /** + * Peeks at the next character without advancing the index. + * @param s the input string + * @param index the current index + * @return the next char + */ + private char peekNext(String s, int index) { + if (index >= 0 && index < s.length() - 1) { + return s.charAt(index + 1); + } + return 0; + } + /** * Record the current position on the token queue as long as * this is a top-level element. Must be called before the @@ -498,7 +576,7 @@ private void recordTokenString(Vector targetStrings) resetTokenMark(tokPos + 1); - if (m_processor.lookahead('(', 1)) + if (m_processor.lookahead(Token.LPAREN, 1)) { int tok = getKeywordToken(m_processor.m_token); @@ -528,14 +606,14 @@ private void recordTokenString(Vector targetStrings) } else { - if (m_processor.tokenIs('@')) + if (m_processor.tokenIs(Token.AT)) { tokPos++; resetTokenMark(tokPos + 1); } - if (m_processor.lookahead(':', 1)) + if (m_processor.lookahead(Token.COLON_CHAR, 1)) { tokPos += 2; } @@ -564,13 +642,13 @@ private final void addToTokenQueue(String s) * @param posOfNSSep The position of the namespace seperator (':'). * @param posOfScan The end of the name index. * - * @throws javax.xml.transform.TransformerException + * @throws TransformerException * * @return -1 always. */ private int mapNSTokens(String pat, int startSubstring, int posOfNSSep, int posOfScan) - throws javax.xml.transform.TransformerException + throws TransformerException { String prefix = ""; diff --git a/jaxp/src/com/sun/org/apache/xpath/internal/compiler/Token.java b/jaxp/src/com/sun/org/apache/xpath/internal/compiler/Token.java new file mode 100644 index 00000000000..51bb2d689c5 --- /dev/null +++ b/jaxp/src/com/sun/org/apache/xpath/internal/compiler/Token.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2022, 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 com.sun.org.apache.xpath.internal.compiler; + +import java.util.Arrays; + +/** + * XPath tokens + */ +public final class Token { + static final char EM = '!'; + static final char EQ = '='; + static final char LT = '<'; + static final char GT = '>'; + static final char PLUS = '+'; + static final char MINUS = '-'; + static final char STAR = '*'; + static final char VBAR = '|'; + static final char SLASH = '/'; + static final char LBRACK = '['; + static final char RBRACK = ']'; + static final char LPAREN = '('; + static final char RPAREN = ')'; + static final char COMMA = ','; + static final char AT = '@'; + static final char US = '_'; + static final char COLON_CHAR = ':'; + static final char SQ = '\''; + static final char DQ = '"'; + static final char DOLLAR = '$'; + + static final String OR = "or"; + static final String AND = "and"; + static final String DIV = "div"; + static final String MOD = "mod"; + static final String QUO = "quo"; + static final String DOT = "."; + static final String DDOT = ".."; + static final String DCOLON = "::"; + static final String ATTR = "attribute"; + static final String CHILD = "child"; + + static final String[] OPERATORS = {OR, AND, DIV, MOD, QUO, + DDOT, DCOLON, ATTR, CHILD}; + + public static boolean contains(String str) { + for (String op : OPERATORS) { + if (op.equals(str)) { + return true; + } + } + return false; + } + + private Token() { + //to prevent instantiation + } +} diff --git a/jaxp/src/com/sun/org/apache/xpath/internal/compiler/XPathParser.java b/jaxp/src/com/sun/org/apache/xpath/internal/compiler/XPathParser.java index e72588a4c4c..64eac0ed33f 100644 --- a/jaxp/src/com/sun/org/apache/xpath/internal/compiler/XPathParser.java +++ b/jaxp/src/com/sun/org/apache/xpath/internal/compiler/XPathParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -20,9 +20,6 @@ package com.sun.org.apache.xpath.internal.compiler; -import javax.xml.transform.ErrorListener; -import javax.xml.transform.TransformerException; - import com.sun.org.apache.xalan.internal.res.XSLMessages; import com.sun.org.apache.xml.internal.utils.PrefixResolver; import com.sun.org.apache.xpath.internal.XPathProcessorException; @@ -30,12 +27,18 @@ import com.sun.org.apache.xpath.internal.objects.XNumber; import com.sun.org.apache.xpath.internal.objects.XString; import com.sun.org.apache.xpath.internal.res.XPATHErrorResources; +import java.util.Objects; +import javax.xml.transform.ErrorListener; +import javax.xml.transform.SourceLocator; +import javax.xml.transform.TransformerException; +import jdk.xml.internal.XMLSecurityManager; +import jdk.xml.internal.XMLSecurityManager.Limit; /** * Tokenizes and parses XPath expressions. This should really be named * XPathParserImpl, and may be renamed in the future. * @xsl.usage general - * @LastModified: May 2019 + * @LastModified: Jan 2022 */ public class XPathParser { @@ -76,13 +79,18 @@ public class XPathParser // counts open predicates private int countPredicate; + // XML security manager + XMLSecurityManager m_xmlSecMgr; + /** * The parser constructor. */ - public XPathParser(ErrorListener errorListener, javax.xml.transform.SourceLocator sourceLocator) + public XPathParser(ErrorListener errorListener, SourceLocator sourceLocator, + XMLSecurityManager xmlSecMgr) { m_errorListener = errorListener; m_sourceLocator = sourceLocator; + m_xmlSecMgr = xmlSecMgr; } /** @@ -100,18 +108,18 @@ public XPathParser(ErrorListener errorListener, javax.xml.transform.SourceLocato * @param namespaceContext An object that is able to resolve prefixes in * the XPath to namespaces. * - * @throws javax.xml.transform.TransformerException + * @throws TransformerException */ public void initXPath( Compiler compiler, String expression, PrefixResolver namespaceContext) - throws javax.xml.transform.TransformerException + throws TransformerException { m_ops = compiler; m_namespaceContext = namespaceContext; m_functionTable = compiler.getFunctionTable(); - Lexer lexer = new Lexer(compiler, namespaceContext, this); + Lexer lexer = new Lexer(compiler, namespaceContext, this, m_xmlSecMgr); lexer.tokenize(expression); @@ -179,18 +187,18 @@ public void initXPath( * @param namespaceContext An object that is able to resolve prefixes in * the XPath to namespaces. * - * @throws javax.xml.transform.TransformerException + * @throws TransformerException */ public void initMatchPattern( Compiler compiler, String expression, PrefixResolver namespaceContext) - throws javax.xml.transform.TransformerException + throws TransformerException { m_ops = compiler; m_namespaceContext = namespaceContext; m_functionTable = compiler.getFunctionTable(); - Lexer lexer = new Lexer(compiler, namespaceContext, this); + Lexer lexer = new Lexer(compiler, namespaceContext, this, m_xmlSecMgr); lexer.tokenize(expression); @@ -383,9 +391,9 @@ private final boolean lookbehindHasToken(int n) if ((m_queueMark - n) > 0) { String lookbehind = (String) m_ops.m_tokenQueue.elementAt(m_queueMark - (n - 1)); - char c0 = (lookbehind == null) ? '|' : lookbehind.charAt(0); + char c0 = (lookbehind == null) ? Token.VBAR : lookbehind.charAt(0); - hasToken = (c0 == '|') ? false : true; + hasToken = (c0 == Token.VBAR) ? false : true; } else { @@ -497,10 +505,10 @@ private final void prevToken() * * @param expected The string to be expected. * - * @throws javax.xml.transform.TransformerException + * @throws TransformerException */ private final void consumeExpected(String expected) - throws javax.xml.transform.TransformerException + throws TransformerException { if (tokenIs(expected)) @@ -525,10 +533,10 @@ private final void consumeExpected(String expected) * * @param expected the character to be expected. * - * @throws javax.xml.transform.TransformerException + * @throws TransformerException */ private final void consumeExpected(char expected) - throws javax.xml.transform.TransformerException + throws TransformerException { if (tokenIs(expected)) @@ -795,9 +803,9 @@ void appendOp(int length, int op) * Expr ::= OrExpr * * - * @throws javax.xml.transform.TransformerException + * @throws TransformerException */ - protected void Expr() throws javax.xml.transform.TransformerException + protected void Expr() throws TransformerException { OrExpr(); } @@ -809,16 +817,16 @@ protected void Expr() throws javax.xml.transform.TransformerException * | OrExpr 'or' AndExpr * * - * @throws javax.xml.transform.TransformerException + * @throws TransformerException */ - protected void OrExpr() throws javax.xml.transform.TransformerException + protected void OrExpr() throws TransformerException { int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH); AndExpr(); - if ((null != m_token) && tokenIs("or")) + if ((null != m_token) && tokenIs(Token.OR)) { nextToken(); insertOp(opPos, 2, OpCodes.OP_OR); @@ -836,16 +844,16 @@ protected void OrExpr() throws javax.xml.transform.TransformerException * | AndExpr 'and' EqualityExpr * * - * @throws javax.xml.transform.TransformerException + * @throws TransformerException */ - protected void AndExpr() throws javax.xml.transform.TransformerException + protected void AndExpr() throws TransformerException { int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH); EqualityExpr(-1); - if ((null != m_token) && tokenIs("and")) + if ((null != m_token) && tokenIs(Token.AND)) { nextToken(); insertOp(opPos, 2, OpCodes.OP_AND); @@ -869,9 +877,9 @@ protected void AndExpr() throws javax.xml.transform.TransformerException * * @return the position at the end of the equality expression. * - * @throws javax.xml.transform.TransformerException + * @throws TransformerException */ - protected int EqualityExpr(int addPos) throws javax.xml.transform.TransformerException + protected int EqualityExpr(int addPos) throws TransformerException { int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH); @@ -883,7 +891,7 @@ protected int EqualityExpr(int addPos) throws javax.xml.transform.TransformerExc if (null != m_token) { - if (tokenIs('!') && lookahead('=', 1)) + if (tokenIs(Token.EM) && lookahead(Token.EQ, 1)) { nextToken(); nextToken(); @@ -896,7 +904,7 @@ protected int EqualityExpr(int addPos) throws javax.xml.transform.TransformerExc m_ops.getOp(addPos + opPlusLeftHandLen + 1) + opPlusLeftHandLen); addPos += 2; } - else if (tokenIs('=')) + else if (tokenIs(Token.EQ)) { nextToken(); insertOp(addPos, 2, OpCodes.OP_EQUALS); @@ -929,9 +937,9 @@ else if (tokenIs('=')) * * @return the position at the end of the relational expression. * - * @throws javax.xml.transform.TransformerException + * @throws TransformerException */ - protected int RelationalExpr(int addPos) throws javax.xml.transform.TransformerException + protected int RelationalExpr(int addPos) throws TransformerException { int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH); @@ -943,11 +951,11 @@ protected int RelationalExpr(int addPos) throws javax.xml.transform.TransformerE if (null != m_token) { - if (tokenIs('<')) + if (tokenIs(Token.LT)) { nextToken(); - if (tokenIs('=')) + if (tokenIs(Token.EQ)) { nextToken(); insertOp(addPos, 2, OpCodes.OP_LTE); @@ -964,11 +972,11 @@ protected int RelationalExpr(int addPos) throws javax.xml.transform.TransformerE m_ops.getOp(addPos + opPlusLeftHandLen + 1) + opPlusLeftHandLen); addPos += 2; } - else if (tokenIs('>')) + else if (tokenIs(Token.GT)) { nextToken(); - if (tokenIs('=')) + if (tokenIs(Token.EQ)) { nextToken(); insertOp(addPos, 2, OpCodes.OP_GTE); @@ -1004,9 +1012,9 @@ else if (tokenIs('>')) * * @return the position at the end of the equality expression. * - * @throws javax.xml.transform.TransformerException + * @throws TransformerException */ - protected int AdditiveExpr(int addPos) throws javax.xml.transform.TransformerException + protected int AdditiveExpr(int addPos) throws TransformerException { int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH); @@ -1018,7 +1026,7 @@ protected int AdditiveExpr(int addPos) throws javax.xml.transform.TransformerExc if (null != m_token) { - if (tokenIs('+')) + if (tokenIs(Token.PLUS)) { nextToken(); insertOp(addPos, 2, OpCodes.OP_PLUS); @@ -1030,7 +1038,7 @@ protected int AdditiveExpr(int addPos) throws javax.xml.transform.TransformerExc m_ops.getOp(addPos + opPlusLeftHandLen + 1) + opPlusLeftHandLen); addPos += 2; } - else if (tokenIs('-')) + else if (tokenIs(Token.MINUS)) { nextToken(); insertOp(addPos, 2, OpCodes.OP_MINUS); @@ -1062,9 +1070,9 @@ else if (tokenIs('-')) * * @return the position at the end of the equality expression. * - * @throws javax.xml.transform.TransformerException + * @throws TransformerException */ - protected int MultiplicativeExpr(int addPos) throws javax.xml.transform.TransformerException + protected int MultiplicativeExpr(int addPos) throws TransformerException { int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH); @@ -1076,7 +1084,7 @@ protected int MultiplicativeExpr(int addPos) throws javax.xml.transform.Transfor if (null != m_token) { - if (tokenIs('*')) + if (tokenIs(Token.STAR)) { nextToken(); insertOp(addPos, 2, OpCodes.OP_MULT); @@ -1088,7 +1096,7 @@ protected int MultiplicativeExpr(int addPos) throws javax.xml.transform.Transfor m_ops.getOp(addPos + opPlusLeftHandLen + 1) + opPlusLeftHandLen); addPos += 2; } - else if (tokenIs("div")) + else if (tokenIs(Token.DIV)) { nextToken(); insertOp(addPos, 2, OpCodes.OP_DIV); @@ -1100,7 +1108,7 @@ else if (tokenIs("div")) m_ops.getOp(addPos + opPlusLeftHandLen + 1) + opPlusLeftHandLen); addPos += 2; } - else if (tokenIs("mod")) + else if (tokenIs(Token.MOD)) { nextToken(); insertOp(addPos, 2, OpCodes.OP_MOD); @@ -1112,7 +1120,7 @@ else if (tokenIs("mod")) m_ops.getOp(addPos + opPlusLeftHandLen + 1) + opPlusLeftHandLen); addPos += 2; } - else if (tokenIs("quo")) + else if (tokenIs(Token.QUO)) { nextToken(); insertOp(addPos, 2, OpCodes.OP_QUO); @@ -1135,15 +1143,15 @@ else if (tokenIs("quo")) * | '-' UnaryExpr * * - * @throws javax.xml.transform.TransformerException + * @throws TransformerException */ - protected void UnaryExpr() throws javax.xml.transform.TransformerException + protected void UnaryExpr() throws TransformerException { int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH); boolean isNeg = false; - if (m_tokenChar == '-') + if (m_tokenChar == Token.MINUS) { nextToken(); appendOp(2, OpCodes.OP_NEG); @@ -1163,9 +1171,9 @@ protected void UnaryExpr() throws javax.xml.transform.TransformerException * StringExpr ::= Expr * * - * @throws javax.xml.transform.TransformerException + * @throws TransformerException */ - protected void StringExpr() throws javax.xml.transform.TransformerException + protected void StringExpr() throws TransformerException { int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH); @@ -1183,9 +1191,9 @@ protected void StringExpr() throws javax.xml.transform.TransformerException * StringExpr ::= Expr * * - * @throws javax.xml.transform.TransformerException + * @throws TransformerException */ - protected void BooleanExpr() throws javax.xml.transform.TransformerException + protected void BooleanExpr() throws TransformerException { int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH); @@ -1209,9 +1217,9 @@ protected void BooleanExpr() throws javax.xml.transform.TransformerException * NumberExpr ::= Expr * * - * @throws javax.xml.transform.TransformerException + * @throws TransformerException */ - protected void NumberExpr() throws javax.xml.transform.TransformerException + protected void NumberExpr() throws TransformerException { int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH); @@ -1234,9 +1242,9 @@ protected void NumberExpr() throws javax.xml.transform.TransformerException * | UnionExpr '|' PathExpr * * - * @throws javax.xml.transform.TransformerException + * @throws TransformerException */ - protected void UnionExpr() throws javax.xml.transform.TransformerException + protected void UnionExpr() throws TransformerException { int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH); @@ -1247,7 +1255,7 @@ protected void UnionExpr() throws javax.xml.transform.TransformerException { PathExpr(); - if (tokenIs('|')) + if (tokenIs(Token.VBAR)) { if (false == foundUnion) { @@ -1280,9 +1288,9 @@ protected void UnionExpr() throws javax.xml.transform.TransformerException * @throws XSLProcessorException thrown if the active ProblemListener and XPathContext decide * the error condition is severe enough to halt processing. * - * @throws javax.xml.transform.TransformerException + * @throws TransformerException */ - protected void PathExpr() throws javax.xml.transform.TransformerException + protected void PathExpr() throws TransformerException { int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH); @@ -1295,7 +1303,7 @@ protected void PathExpr() throws javax.xml.transform.TransformerException // have been inserted. boolean locationPathStarted = (filterExprMatch==FILTER_MATCH_PREDICATES); - if (tokenIs('/')) + if (tokenIs(Token.SLASH)) { nextToken(); @@ -1345,9 +1353,9 @@ protected void PathExpr() throws javax.xml.transform.TransformerException * FilterExpr that was just a PrimaryExpr; or * FILTER_MATCH_FAILED, if this method did not match a FilterExpr * - * @throws javax.xml.transform.TransformerException + * @throws TransformerException */ - protected int FilterExpr() throws javax.xml.transform.TransformerException + protected int FilterExpr() throws TransformerException { int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH); @@ -1356,13 +1364,13 @@ protected int FilterExpr() throws javax.xml.transform.TransformerException if (PrimaryExpr()) { - if (tokenIs('[')) + if (tokenIs(Token.LBRACK)) { // int locationPathOpPos = opPos; insertOp(opPos, 2, OpCodes.OP_LOCATIONPATH); - while (tokenIs('[')) + while (tokenIs(Token.LBRACK)) { Predicate(); } @@ -1400,16 +1408,16 @@ protected int FilterExpr() throws javax.xml.transform.TransformerException * * @return true if this method successfully matched a PrimaryExpr * - * @throws javax.xml.transform.TransformerException + * @throws TransformerException * */ - protected boolean PrimaryExpr() throws javax.xml.transform.TransformerException + protected boolean PrimaryExpr() throws TransformerException { boolean matchFound; int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH); - if ((m_tokenChar == '\'') || (m_tokenChar == '"')) + if ((m_tokenChar == Token.SQ) || (m_tokenChar == Token.DQ)) { appendOp(2, OpCodes.OP_LITERAL); Literal(); @@ -1419,7 +1427,7 @@ protected boolean PrimaryExpr() throws javax.xml.transform.TransformerException matchFound = true; } - else if (m_tokenChar == '$') + else if (m_tokenChar == Token.DOLLAR) { nextToken(); // consume '$' appendOp(2, OpCodes.OP_VARIABLE); @@ -1430,12 +1438,12 @@ else if (m_tokenChar == '$') matchFound = true; } - else if (m_tokenChar == '(') + else if (m_tokenChar == Token.LPAREN) { nextToken(); appendOp(2, OpCodes.OP_GROUP); Expr(); - consumeExpected(')'); + consumeExpected(Token.RPAREN); m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos); @@ -1453,7 +1461,7 @@ else if ((null != m_token) && ((('.' == m_tokenChar) && (m_token.length() > 1) & matchFound = true; } - else if (lookahead('(', 1) || (lookahead(':', 1) && lookahead('(', 3))) + else if (lookahead(Token.LPAREN, 1) || (lookahead(Token.COLON_CHAR, 1) && lookahead(Token.LPAREN, 3))) { matchFound = FunctionCall(); } @@ -1470,9 +1478,9 @@ else if (lookahead('(', 1) || (lookahead(':', 1) && lookahead('(', 3))) * Argument ::= Expr * * - * @throws javax.xml.transform.TransformerException + * @throws TransformerException */ - protected void Argument() throws javax.xml.transform.TransformerException + protected void Argument() throws TransformerException { int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH); @@ -1490,14 +1498,14 @@ protected void Argument() throws javax.xml.transform.TransformerException * * @return true if, and only if, a FunctionCall was matched * - * @throws javax.xml.transform.TransformerException + * @throws TransformerException */ - protected boolean FunctionCall() throws javax.xml.transform.TransformerException + protected boolean FunctionCall() throws TransformerException { int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH); - if (lookahead(':', 1)) + if (lookahead(Token.COLON_CHAR, 1)) { appendOp(4, OpCodes.OP_EXTFUNCTION); @@ -1537,22 +1545,23 @@ protected boolean FunctionCall() throws javax.xml.transform.TransformerException nextToken(); } - consumeExpected('('); + consumeExpected(Token.LPAREN); - while (!tokenIs(')') && m_token != null) + while (!tokenIs(Token.RPAREN) && m_token != null) { - if (tokenIs(',')) + if (tokenIs(Token.COMMA)) { - error(XPATHErrorResources.ER_FOUND_COMMA_BUT_NO_PRECEDING_ARG, null); //"Found ',' but no preceding argument!"); + //"Found ',' but no preceding argument!"); + error(XPATHErrorResources.ER_FOUND_COMMA_BUT_NO_PRECEDING_ARG, null); } Argument(); - if (!tokenIs(')')) + if (!tokenIs(Token.RPAREN)) { - consumeExpected(','); + consumeExpected(Token.COMMA); - if (tokenIs(')')) + if (tokenIs(Token.RPAREN)) { error(XPATHErrorResources.ER_FOUND_COMMA_BUT_NO_FOLLOWING_ARG, null); //"Found ',' but no following argument!"); @@ -1560,7 +1569,7 @@ protected boolean FunctionCall() throws javax.xml.transform.TransformerException } } - consumeExpected(')'); + consumeExpected(Token.RPAREN); // Terminate for safety. m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), OpCodes.ENDOP); @@ -1579,9 +1588,9 @@ protected boolean FunctionCall() throws javax.xml.transform.TransformerException * | AbsoluteLocationPath * * - * @throws javax.xml.transform.TransformerException + * @throws TransformerException */ - protected void LocationPath() throws javax.xml.transform.TransformerException + protected void LocationPath() throws TransformerException { int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH); @@ -1589,7 +1598,7 @@ protected void LocationPath() throws javax.xml.transform.TransformerException // int locationPathOpPos = opPos; appendOp(2, OpCodes.OP_LOCATIONPATH); - boolean seenSlash = tokenIs('/'); + boolean seenSlash = tokenIs(Token.SLASH); if (seenSlash) { @@ -1630,17 +1639,17 @@ protected void LocationPath() throws javax.xml.transform.TransformerException * * @returns true if, and only if, a RelativeLocationPath was matched * - * @throws javax.xml.transform.TransformerException + * @throws TransformerException */ protected boolean RelativeLocationPath() - throws javax.xml.transform.TransformerException + throws TransformerException { if (!Step()) { return false; } - while (tokenIs('/')) + while (tokenIs(Token.SLASH)) { nextToken(); @@ -1662,13 +1671,13 @@ protected boolean RelativeLocationPath() * * @returns false if step was empty (or only a '/'); true, otherwise * - * @throws javax.xml.transform.TransformerException + * @throws TransformerException */ - protected boolean Step() throws javax.xml.transform.TransformerException + protected boolean Step() throws TransformerException { int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH); - boolean doubleSlash = tokenIs('/'); + boolean doubleSlash = tokenIs(Token.SLASH); // At most a single '/' before each Step is consumed by caller; if the // first thing is a '/', that means we had '//' and the Step must not @@ -1700,11 +1709,11 @@ protected boolean Step() throws javax.xml.transform.TransformerException opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH); } - if (tokenIs(".")) + if (tokenIs(Token.DOT)) { nextToken(); - if (tokenIs('[')) + if (tokenIs(Token.LBRACK)) { error(XPATHErrorResources.ER_PREDICATE_ILLEGAL_SYNTAX, null); //"'..[predicate]' or '.[predicate]' is illegal syntax. Use 'self::node()[predicate]' instead."); } @@ -1715,7 +1724,7 @@ protected boolean Step() throws javax.xml.transform.TransformerException m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH) - 2,4); m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH) - 1, OpCodes.NODETYPE_NODE); } - else if (tokenIs("..")) + else if (tokenIs(Token.DDOT)) { nextToken(); appendOp(4, OpCodes.FROM_PARENT); @@ -1728,12 +1737,12 @@ else if (tokenIs("..")) // There is probably a better way to test for this // transition... but it gets real hairy if you try // to do it in basis(). - else if (tokenIs('*') || tokenIs('@') || tokenIs('_') + else if (tokenIs(Token.STAR) || tokenIs(Token.AT) || tokenIs(Token.US) || (m_token!= null && Character.isLetter(m_token.charAt(0)))) { Basis(); - while (tokenIs('[')) + while (tokenIs(Token.LBRACK)) { Predicate(); } @@ -1762,23 +1771,23 @@ else if (tokenIs('*') || tokenIs('@') || tokenIs('_') * Basis ::= AxisName '::' NodeTest * | AbbreviatedBasis * - * @throws javax.xml.transform.TransformerException + * @throws TransformerException */ - protected void Basis() throws javax.xml.transform.TransformerException + protected void Basis() throws TransformerException { int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH); int axesType; // The next blocks guarantee that a FROM_XXX will be added. - if (lookahead("::", 1)) + if (lookahead(Token.DCOLON, 1)) { axesType = AxisName(); nextToken(); nextToken(); } - else if (tokenIs('@')) + else if (tokenIs(Token.AT)) { axesType = OpCodes.FROM_ATTRIBUTES; @@ -1809,9 +1818,9 @@ else if (tokenIs('@')) * * @return FROM_XXX axes type, found in {@link com.sun.org.apache.xpath.internal.compiler.Keywords}. * - * @throws javax.xml.transform.TransformerException + * @throws TransformerException */ - protected int AxisName() throws javax.xml.transform.TransformerException + protected int AxisName() throws TransformerException { Object val = Keywords.getAxisName(m_token); @@ -1837,12 +1846,12 @@ protected int AxisName() throws javax.xml.transform.TransformerException * * @param axesType FROM_XXX axes type, found in {@link com.sun.org.apache.xpath.internal.compiler.Keywords}. * - * @throws javax.xml.transform.TransformerException + * @throws TransformerException */ - protected void NodeTest(int axesType) throws javax.xml.transform.TransformerException + protected void NodeTest(int axesType) throws TransformerException { - if (lookahead('(', 1)) + if (lookahead(Token.LPAREN, 1)) { Object nodeTestOp = Keywords.getNodeType(m_token); @@ -1860,17 +1869,17 @@ protected void NodeTest(int axesType) throws javax.xml.transform.TransformerExce m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), nt); m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1); - consumeExpected('('); + consumeExpected(Token.LPAREN); if (OpCodes.NODETYPE_PI == nt) { - if (!tokenIs(')')) + if (!tokenIs(Token.RPAREN)) { Literal(); } } - consumeExpected(')'); + consumeExpected(Token.RPAREN); } } else @@ -1880,9 +1889,9 @@ protected void NodeTest(int axesType) throws javax.xml.transform.TransformerExce m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), OpCodes.NODENAME); m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1); - if (lookahead(':', 1)) + if (lookahead(Token.COLON_CHAR, 1)) { - if (tokenIs('*')) + if (tokenIs(Token.STAR)) { m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), OpCodes.ELEMWILDCARD); } @@ -1892,7 +1901,7 @@ protected void NodeTest(int axesType) throws javax.xml.transform.TransformerExce // Minimalist check for an NCName - just check first character // to distinguish from other possible tokens - if (!Character.isLetter(m_tokenChar) && !tokenIs('_')) + if (!Character.isLetter(m_tokenChar) && !tokenIs(Token.US)) { // "Node test that matches either NCName:* or QName was expected." error(XPATHErrorResources.ER_EXPECTED_NODE_TEST, null); @@ -1909,7 +1918,7 @@ protected void NodeTest(int axesType) throws javax.xml.transform.TransformerExce m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1); - if (tokenIs('*')) + if (tokenIs(Token.STAR)) { m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), OpCodes.ELEMWILDCARD); } @@ -1919,7 +1928,7 @@ protected void NodeTest(int axesType) throws javax.xml.transform.TransformerExce // Minimalist check for an NCName - just check first character // to distinguish from other possible tokens - if (!Character.isLetter(m_tokenChar) && !tokenIs('_')) + if (!Character.isLetter(m_tokenChar) && !tokenIs(Token.US)) { // "Node test that matches either NCName:* or QName was expected." error(XPATHErrorResources.ER_EXPECTED_NODE_TEST, null); @@ -1937,11 +1946,11 @@ protected void NodeTest(int axesType) throws javax.xml.transform.TransformerExce * Predicate ::= '[' PredicateExpr ']' * * - * @throws javax.xml.transform.TransformerException + * @throws TransformerException */ - protected void Predicate() throws javax.xml.transform.TransformerException + protected void Predicate() throws TransformerException { - if (tokenIs('[')) + if (tokenIs(Token.LBRACK)) { countPredicate++; nextToken(); @@ -1956,9 +1965,9 @@ protected void Predicate() throws javax.xml.transform.TransformerException * PredicateExpr ::= Expr * * - * @throws javax.xml.transform.TransformerException + * @throws TransformerException */ - protected void PredicateExpr() throws javax.xml.transform.TransformerException + protected void PredicateExpr() throws TransformerException { int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH); @@ -1978,12 +1987,12 @@ protected void PredicateExpr() throws javax.xml.transform.TransformerException * Prefix ::= NCName * LocalPart ::= NCName * - * @throws javax.xml.transform.TransformerException + * @throws TransformerException */ - protected void QName() throws javax.xml.transform.TransformerException + protected void QName() throws TransformerException { // Namespace - if(lookahead(':', 1)) + if(lookahead(Token.COLON_CHAR, 1)) { m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), m_queueMark - 1); m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1); @@ -2025,16 +2034,16 @@ protected void NCName() * | "'" [^']* "'" * * - * @throws javax.xml.transform.TransformerException + * @throws TransformerException */ - protected void Literal() throws javax.xml.transform.TransformerException + protected void Literal() throws TransformerException { int last = m_token.length() - 1; char c0 = m_tokenChar; char cX = m_token.charAt(last); - if (((c0 == '\"') && (cX == '\"')) || ((c0 == '\'') && (cX == '\''))) + if (((c0 == Token.DQ) && (cX == Token.DQ)) || ((c0 == Token.SQ) && (cX == Token.SQ))) { // Mutate the token to remove the quotes and have the XString object @@ -2065,9 +2074,9 @@ protected void Literal() throws javax.xml.transform.TransformerException * Number ::= [0-9]+('.'[0-9]+)? | '.'[0-9]+ * * - * @throws javax.xml.transform.TransformerException + * @throws TransformerException */ - protected void Number() throws javax.xml.transform.TransformerException + protected void Number() throws TransformerException { if (null != m_token) @@ -2108,16 +2117,16 @@ protected void Number() throws javax.xml.transform.TransformerException * | Pattern '|' LocationPathPattern * * - * @throws javax.xml.transform.TransformerException + * @throws TransformerException */ - protected void Pattern() throws javax.xml.transform.TransformerException + protected void Pattern() throws TransformerException { while (true) { LocationPathPattern(); - if (tokenIs('|')) + if (tokenIs(Token.VBAR)) { nextToken(); } @@ -2136,9 +2145,9 @@ protected void Pattern() throws javax.xml.transform.TransformerException * | '//'? RelativePathPattern * * - * @throws javax.xml.transform.TransformerException + * @throws TransformerException */ - protected void LocationPathPattern() throws javax.xml.transform.TransformerException + protected void LocationPathPattern() throws TransformerException { int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH); @@ -2151,17 +2160,17 @@ protected void LocationPathPattern() throws javax.xml.transform.TransformerExcep appendOp(2, OpCodes.OP_LOCATIONPATHPATTERN); - if (lookahead('(', 1) + if (lookahead(Token.LPAREN, 1) && (tokenIs(Keywords.FUNC_ID_STRING) || tokenIs(Keywords.FUNC_KEY_STRING))) { IdKeyPattern(); - if (tokenIs('/')) + if (tokenIs(Token.SLASH)) { nextToken(); - if (tokenIs('/')) + if (tokenIs(Token.SLASH)) { appendOp(4, OpCodes.MATCH_ANY_ANCESTOR); @@ -2179,9 +2188,9 @@ protected void LocationPathPattern() throws javax.xml.transform.TransformerExcep relativePathStatus = RELATIVE_PATH_REQUIRED; } } - else if (tokenIs('/')) + else if (tokenIs(Token.SLASH)) { - if (lookahead('/', 1)) + if (lookahead(Token.SLASH, 1)) { appendOp(4, OpCodes.MATCH_ANY_ANCESTOR); @@ -2214,7 +2223,7 @@ else if (tokenIs('/')) if (relativePathStatus != RELATIVE_PATH_NOT_PERMITTED) { - if (!tokenIs('|') && (null != m_token)) + if (!tokenIs(Token.VBAR) && (null != m_token)) { RelativePathPattern(); } @@ -2239,9 +2248,9 @@ else if (relativePathStatus == RELATIVE_PATH_REQUIRED) * (Also handle doc()) * * - * @throws javax.xml.transform.TransformerException + * @throws TransformerException */ - protected void IdKeyPattern() throws javax.xml.transform.TransformerException + protected void IdKeyPattern() throws TransformerException { FunctionCall(); } @@ -2252,17 +2261,17 @@ protected void IdKeyPattern() throws javax.xml.transform.TransformerException * | RelativePathPattern '/' StepPattern * | RelativePathPattern '//' StepPattern * - * @throws javax.xml.transform.TransformerException + * @throws TransformerException */ protected void RelativePathPattern() - throws javax.xml.transform.TransformerException + throws TransformerException { // Caller will have consumed any '/' or '//' preceding the // RelativePathPattern, so let StepPattern know it can't begin with a '/' boolean trailingSlashConsumed = StepPattern(false); - while (tokenIs('/')) + while (tokenIs(Token.SLASH)) { nextToken(); @@ -2282,10 +2291,10 @@ protected void RelativePathPattern() * * @return boolean indicating whether a slash following the step was consumed * - * @throws javax.xml.transform.TransformerException + * @throws TransformerException */ protected boolean StepPattern(boolean isLeadingSlashPermitted) - throws javax.xml.transform.TransformerException + throws TransformerException { return AbbreviatedNodeTestStep(isLeadingSlashPermitted); } @@ -2299,10 +2308,10 @@ protected boolean StepPattern(boolean isLeadingSlashPermitted) * * @return boolean indicating whether a slash following the step was consumed * - * @throws javax.xml.transform.TransformerException + * @throws TransformerException */ protected boolean AbbreviatedNodeTestStep(boolean isLeadingSlashPermitted) - throws javax.xml.transform.TransformerException + throws TransformerException { int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH); @@ -2311,22 +2320,22 @@ protected boolean AbbreviatedNodeTestStep(boolean isLeadingSlashPermitted) // The next blocks guarantee that a MATCH_XXX will be added. int matchTypePos = -1; - if (tokenIs('@')) + if (tokenIs(Token.AT)) { axesType = OpCodes.MATCH_ATTRIBUTE; appendOp(2, axesType); nextToken(); } - else if (this.lookahead("::", 1)) + else if (this.lookahead(Token.DCOLON, 1)) { - if (tokenIs("attribute")) + if (tokenIs(Token.ATTR)) { axesType = OpCodes.MATCH_ATTRIBUTE; appendOp(2, axesType); } - else if (tokenIs("child")) + else if (tokenIs(Token.CHILD)) { matchTypePos = m_ops.getOp(OpMap.MAPINDEX_LENGTH); axesType = OpCodes.MATCH_IMMEDIATE_ANCESTOR; @@ -2344,7 +2353,7 @@ else if (tokenIs("child")) nextToken(); nextToken(); } - else if (tokenIs('/')) + else if (tokenIs(Token.SLASH)) { if (!isLeadingSlashPermitted) { @@ -2373,7 +2382,7 @@ else if (tokenIs('/')) m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH + 1, m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos); - while (tokenIs('[')) + while (tokenIs(Token.LBRACK)) { Predicate(); } @@ -2392,7 +2401,7 @@ else if (tokenIs('/')) // If current step is on the attribute axis (e.g., "@x//b"), we won't // change the current step, and let following step be marked as // MATCH_ANY_ANCESTOR on next call instead. - if ((matchTypePos > -1) && tokenIs('/') && lookahead('/', 1)) + if ((matchTypePos > -1) && tokenIs(Token.SLASH) && lookahead(Token.SLASH, 1)) { m_ops.setOp(matchTypePos, OpCodes.MATCH_ANY_ANCESTOR); diff --git a/jaxp/src/com/sun/org/apache/xpath/internal/jaxp/XPathFactoryImpl.java b/jaxp/src/com/sun/org/apache/xpath/internal/jaxp/XPathFactoryImpl.java index 5bb7468c60e..0f1a2262c3e 100644 --- a/jaxp/src/com/sun/org/apache/xpath/internal/jaxp/XPathFactoryImpl.java +++ b/jaxp/src/com/sun/org/apache/xpath/internal/jaxp/XPathFactoryImpl.java @@ -30,6 +30,7 @@ import javax.xml.xpath.XPathFunctionResolver; import javax.xml.xpath.XPathVariableResolver; import jdk.xml.internal.JdkXmlFeatures; +import jdk.xml.internal.XMLSecurityManager; /** * The XPathFactory builds XPaths. @@ -67,6 +68,12 @@ public class XPathFactoryImpl extends XPathFactory { */ private final JdkXmlFeatures _featureManager; + + /** + * The XML security manager + */ + private XMLSecurityManager _xmlSecMgr; + /** * javax.xml.xpath.XPathFactory implementation. */ @@ -77,6 +84,7 @@ public XPathFactoryImpl() { _isNotSecureProcessing = false; } _featureManager = new JdkXmlFeatures(!_isNotSecureProcessing); + _xmlSecMgr = new XMLSecurityManager(true); } /** *

Is specified object model supported by this @@ -126,7 +134,7 @@ public boolean isObjectModelSupported(String objectModel) { public javax.xml.xpath.XPath newXPath() { return new com.sun.org.apache.xpath.internal.jaxp.XPathImpl( xPathVariableResolver, xPathFunctionResolver, - !_isNotSecureProcessing, _featureManager ); + !_isNotSecureProcessing, _featureManager, _xmlSecMgr); } /** diff --git a/jaxp/src/com/sun/org/apache/xpath/internal/jaxp/XPathImpl.java b/jaxp/src/com/sun/org/apache/xpath/internal/jaxp/XPathImpl.java index 8c6437a0c83..8d15deaad30 100644 --- a/jaxp/src/com/sun/org/apache/xpath/internal/jaxp/XPathImpl.java +++ b/jaxp/src/com/sun/org/apache/xpath/internal/jaxp/XPathImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -46,6 +46,7 @@ import java.io.IOException; import jdk.xml.internal.JdkXmlFeatures; +import jdk.xml.internal.XMLSecurityManager; import jdk.xml.internal.JdkXmlUtils; /** @@ -71,19 +72,23 @@ public class XPathImpl implements javax.xml.xpath.XPath { private boolean featureSecureProcessing = false; private boolean overrideDefaultParser = true; private final JdkXmlFeatures featureManager; + XMLSecurityManager xmlSecMgr; - XPathImpl( XPathVariableResolver vr, XPathFunctionResolver fr ) { - this(vr, fr, false, new JdkXmlFeatures(false)); + XPathImpl(XPathVariableResolver vr, XPathFunctionResolver fr) { + this(vr, fr, false, new JdkXmlFeatures(false), new XMLSecurityManager(true)); } - XPathImpl( XPathVariableResolver vr, XPathFunctionResolver fr, - boolean featureSecureProcessing, JdkXmlFeatures featureManager) { + XPathImpl(XPathVariableResolver vr, XPathFunctionResolver fr, + boolean featureSecureProcessing, JdkXmlFeatures featureManager, + XMLSecurityManager xmlSecMgr) { this.origVariableResolver = this.variableResolver = vr; this.origFunctionResolver = this.functionResolver = fr; this.featureSecureProcessing = featureSecureProcessing; this.featureManager = featureManager; this.overrideDefaultParser = featureManager.getFeature( JdkXmlFeatures.XmlFeature.JDK_OVERRIDE_PARSER); + + this.xmlSecMgr = xmlSecMgr; } /** @@ -186,7 +191,7 @@ private DocumentBuilder getParser() { private XObject eval(String expression, Object contextItem) throws javax.xml.transform.TransformerException { com.sun.org.apache.xpath.internal.XPath xpath = new com.sun.org.apache.xpath.internal.XPath( expression, - null, prefixResolver, com.sun.org.apache.xpath.internal.XPath.SELECT ); + null, prefixResolver, com.sun.org.apache.xpath.internal.XPath.SELECT, null, null, xmlSecMgr); com.sun.org.apache.xpath.internal.XPathContext xpathSupport = null; if ( functionResolver != null ) { JAXPExtensionsProvider jep = new JAXPExtensionsProvider( @@ -387,7 +392,7 @@ public XPathExpression compile(String expression) } try { com.sun.org.apache.xpath.internal.XPath xpath = new XPath (expression, null, - prefixResolver, com.sun.org.apache.xpath.internal.XPath.SELECT ); + prefixResolver, com.sun.org.apache.xpath.internal.XPath.SELECT, null, null, xmlSecMgr); // Can have errorListener XPathExpressionImpl ximpl = new XPathExpressionImpl(xpath, prefixResolver, functionResolver, variableResolver, diff --git a/jaxp/src/com/sun/org/apache/xpath/internal/res/XPATHErrorResources.java b/jaxp/src/com/sun/org/apache/xpath/internal/res/XPATHErrorResources.java index 021a9b6415b..cdea6d69976 100644 --- a/jaxp/src/com/sun/org/apache/xpath/internal/res/XPATHErrorResources.java +++ b/jaxp/src/com/sun/org/apache/xpath/internal/res/XPATHErrorResources.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -31,7 +31,7 @@ * Also you need to update the count of messages(MAX_CODE)or * the count of warnings(MAX_WARNING) [ Information purpose only] * @xsl.usage advanced - * @LastModified: May 2019 + * @LastModified: Jan 2022 */ public class XPATHErrorResources extends ListResourceBundle { @@ -321,6 +321,9 @@ public class XPATHErrorResources extends ListResourceBundle public static final String ER_SECUREPROCESSING_FEATURE = "ER_SECUREPROCESSING_FEATURE"; public static final String ER_NULL_XPATH_FUNCTION_RESOLVER = "ER_NULL_XPATH_FUNCTION_RESOLVER"; public static final String ER_NULL_XPATH_VARIABLE_RESOLVER = "ER_NULL_XPATH_VARIABLE_RESOLVER"; + public static final String ER_XPATH_GROUP_LIMIT = "XPATH_GROUP_LIMIT"; + public static final String ER_XPATH_OPERATOR_LIMIT = "XPATH_OPERATOR_LIMIT"; + //END: Keys needed for exception messages of JAXP 1.3 XPath API implementation public static final String WG_LOCALE_NAME_NOT_HANDLED = @@ -832,6 +835,14 @@ public class XPATHErrorResources extends ListResourceBundle { ER_NULL_XPATH_VARIABLE_RESOLVER, "Attempting to set a null XPathVariableResolver:{0}#setXPathVariableResolver(null)"}, + { ER_XPATH_GROUP_LIMIT, + "JAXP0801001: the compiler encountered an XPath expression containing " + + "''{0}'' groups that exceeds the ''{1}'' limit set by ''{2}''."}, + + { ER_XPATH_OPERATOR_LIMIT, + "JAXP0801002: the compiler encountered an XPath expression containing " + + "''{0}'' operators that exceeds the ''{1}'' limit set by ''{2}''."}, + //END: Definitions of error keys used in exception messages of JAXP 1.3 XPath API implementation // Warnings... diff --git a/jaxp/src/com/sun/xml/internal/stream/events/EntityDeclarationImpl.java b/jaxp/src/com/sun/xml/internal/stream/events/EntityDeclarationImpl.java index e4b36ece592..638063283de 100644 --- a/jaxp/src/com/sun/xml/internal/stream/events/EntityDeclarationImpl.java +++ b/jaxp/src/com/sun/xml/internal/stream/events/EntityDeclarationImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2021, 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 @@ -28,6 +28,7 @@ import javax.xml.stream.events.EntityDeclaration; import javax.xml.stream.events.XMLEvent; import com.sun.org.apache.xerces.internal.xni.XMLResourceIdentifier; +import jdk.xml.internal.JdkXmlUtils; /** * @@ -129,18 +130,12 @@ protected void writeAsEncodedUnicodeEx(java.io.Writer writer) //escape quotes, lt and amps writer.write(" \""); charEncode(writer, fReplacementText); + writer.write("\""); } else { //external entity - String pubId = getPublicId(); - if (pubId != null) { - writer.write(" PUBLIC \""); - writer.write(pubId); - } else { - writer.write(" SYSTEM \""); - writer.write(getSystemId()); - } + writer.write(JdkXmlUtils.getDTDExternalDecl(getPublicId(), getSystemId())); } - writer.write("\""); + if (fNotationName != null) { writer.write(" NDATA "); writer.write(fNotationName); diff --git a/jaxp/src/com/sun/xml/internal/stream/events/NotationDeclarationImpl.java b/jaxp/src/com/sun/xml/internal/stream/events/NotationDeclarationImpl.java index af3d949b1a5..be0caee0cab 100644 --- a/jaxp/src/com/sun/xml/internal/stream/events/NotationDeclarationImpl.java +++ b/jaxp/src/com/sun/xml/internal/stream/events/NotationDeclarationImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2021, 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 @@ -28,6 +28,7 @@ import javax.xml.stream.events.NotationDeclaration; import javax.xml.stream.events.XMLEvent; import com.sun.xml.internal.stream.dtd.nonvalidating.XMLNotationDecl; +import jdk.xml.internal.JdkXmlUtils; /** * Implementation of NotationDeclaration event. @@ -88,16 +89,7 @@ protected void writeAsEncodedUnicodeEx(java.io.Writer writer) { writer.write("'); } } diff --git a/jaxp/src/jdk/xml/internal/JdkXmlUtils.java b/jaxp/src/jdk/xml/internal/JdkXmlUtils.java index 012fa7711a9..767d2e176e9 100644 --- a/jaxp/src/jdk/xml/internal/JdkXmlUtils.java +++ b/jaxp/src/jdk/xml/internal/JdkXmlUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2022, 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 @@ -25,7 +25,6 @@ package jdk.xml.internal; -import com.sun.org.apache.xalan.internal.utils.XMLSecurityManager; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import com.sun.org.apache.xerces.internal.impl.Constants; import com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl; @@ -250,19 +249,40 @@ public static SAXTransformerFactory getSAXTransformFactory(boolean overrideDefau } /** - * Returns the character to be used to quote the input content. Between - * single and double quotes, this method returns the one that is not found - * in the input. Returns double quote by default. + * Returns the external declaration for a DTD construct. * - * @param s the input string - * @return returns the quote not found in the input + * @param publicId the public identifier + * @param systemId the system identifier + * @return a DTD external declaration */ - public static char getQuoteChar(String s) { - if (s != null && s.indexOf('"') > -1) { - return '\''; - } else { - return '"'; + public static String getDTDExternalDecl(String publicId, String systemId) { + StringBuilder sb = new StringBuilder(); + if (null != publicId) { + sb.append(" PUBLIC "); + sb.append(quoteString(publicId)); + } + + if (null != systemId) { + if (null == publicId) { + sb.append(" SYSTEM "); + } else { + sb.append(" "); + } + + sb.append(quoteString(systemId)); } + return sb.toString(); + } + + /** + * Returns the input string quoted with double quotes or single ones if + * there is a double quote in the string. + * @param s the input string, can not be null + * @return the quoted string + */ + private static String quoteString(String s) { + char c = (s.indexOf('"') > -1) ? '\'' : '"'; + return c + s + c; } private static XMLReader getXMLReaderWSAXFactory(boolean overrideDefaultParser) { diff --git a/jaxp/src/jdk/xml/internal/XMLLimitAnalyzer.java b/jaxp/src/jdk/xml/internal/XMLLimitAnalyzer.java new file mode 100644 index 00000000000..18ce1b665fd --- /dev/null +++ b/jaxp/src/jdk/xml/internal/XMLLimitAnalyzer.java @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2013, 2022, 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.util.Formatter; +import java.util.HashMap; +import java.util.Map; +import jdk.xml.internal.XMLSecurityManager.Limit; +import com.sun.org.apache.xalan.internal.XalanConstants; + + +/** + * A helper for analyzing entity expansion limits + * + */ +public final class XMLLimitAnalyzer { + + /** + * Map old property names with the new ones + */ + public static enum NameMap { + ENTITY_EXPANSION_LIMIT(XalanConstants.SP_ENTITY_EXPANSION_LIMIT, XalanConstants.ENTITY_EXPANSION_LIMIT), + MAX_OCCUR_NODE_LIMIT(XalanConstants.SP_MAX_OCCUR_LIMIT, XalanConstants.MAX_OCCUR_LIMIT), + ELEMENT_ATTRIBUTE_LIMIT(XalanConstants.SP_ELEMENT_ATTRIBUTE_LIMIT, XalanConstants.ELEMENT_ATTRIBUTE_LIMIT); + + final String newName; + final String oldName; + + NameMap(String newName, String oldName) { + this.newName = newName; + this.oldName = oldName; + } + + String getOldName(String newName) { + if (newName.equals(this.newName)) { + return oldName; + } + return null; + } + } + + /** + * Max value accumulated for each property + */ + private final int[] values; + /** + * Names of the entities corresponding to their max values + */ + private final String[] names; + /** + * Total value of accumulated entities + */ + private final int[] totalValue; + + /** + * Maintain values of the top 10 elements in the process of parsing + */ + private final Map[] caches; + + private String entityStart, entityEnd; + /** + * Default constructor. Establishes default values for known security + * vulnerabilities. + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + public XMLLimitAnalyzer() { + values = new int[Limit.values().length]; + totalValue = new int[Limit.values().length]; + names = new String[Limit.values().length]; + caches = new Map[Limit.values().length]; + } + + /** + * Add the value to the current max count for the specified property + * To find the max value of all entities, set no limit + * + * @param limit the type of the property + * @param entityName the name of the entity + * @param value the value of the entity + */ + public void addValue(Limit limit, String entityName, int value) { + addValue(limit.ordinal(), entityName, value); + } + + /** + * Add the value to the current count by the index of the property + * @param index the index of the property + * @param entityName the name of the entity + * @param value the value of the entity + */ + public void addValue(int index, String entityName, int value) { + if (index == Limit.ENTITY_EXPANSION_LIMIT.ordinal() || + index == Limit.MAX_OCCUR_NODE_LIMIT.ordinal() || + index == Limit.ELEMENT_ATTRIBUTE_LIMIT.ordinal() || + index == Limit.TOTAL_ENTITY_SIZE_LIMIT.ordinal() || + index == Limit.ENTITY_REPLACEMENT_LIMIT.ordinal() + ) { + totalValue[index] += value; + return; + } + if (index == Limit.MAX_ELEMENT_DEPTH_LIMIT.ordinal() || + index == Limit.MAX_NAME_LIMIT.ordinal()) { + values[index] = value; + totalValue[index] = value; + return; + } + + Map cache; + if (caches[index] == null) { + cache = new HashMap<>(10); + caches[index] = cache; + } else { + cache = caches[index]; + } + + int accumulatedValue = value; + if (cache.containsKey(entityName)) { + accumulatedValue += cache.get(entityName); + cache.put(entityName, accumulatedValue); + } else { + cache.put(entityName, value); + } + + if (accumulatedValue > values[index]) { + values[index] = accumulatedValue; + names[index] = entityName; + } + + + if (index == Limit.GENERAL_ENTITY_SIZE_LIMIT.ordinal() || + index == Limit.PARAMETER_ENTITY_SIZE_LIMIT.ordinal()) { + totalValue[Limit.TOTAL_ENTITY_SIZE_LIMIT.ordinal()] += value; + } + } + + /** + * Return the value of the current max count for the specified property + * + * @param limit the property + * @return the value of the property + */ + public int getValue(Limit limit) { + return getValue(limit.ordinal()); + } + + public int getValue(int index) { + if (index == Limit.ENTITY_REPLACEMENT_LIMIT.ordinal()) { + return totalValue[index]; + } + return values[index]; + } + /** + * Return the total value accumulated so far + * + * @param limit the property + * @return the accumulated value of the property + */ + public int getTotalValue(Limit limit) { + return totalValue[limit.ordinal()]; + } + + public int getTotalValue(int index) { + return totalValue[index]; + } + /** + * Return the current max value (count or length) by the index of a property + * @param index the index of a property + * @return count of a property + */ + public int getValueByIndex(int index) { + return values[index]; + } + + public void startEntity(String name) { + entityStart = name; + } + + public boolean isTracking(String name) { + if (entityStart == null) { + return false; + } + return entityStart.equals(name); + } + /** + * Stop tracking the entity + * @param limit the limit property + * @param name the name of an entity + */ + public void endEntity(Limit limit, String name) { + entityStart = ""; + Map cache = caches[limit.ordinal()]; + if (cache != null) { + cache.remove(name); + } + } + + /** + * Resets the current value of the specified limit. + * @param limit The limit to be reset. + */ + public void reset(Limit limit) { + if (limit.ordinal() == Limit.TOTAL_ENTITY_SIZE_LIMIT.ordinal()) { + totalValue[limit.ordinal()] = 0; + } else if (limit.ordinal() == Limit.GENERAL_ENTITY_SIZE_LIMIT.ordinal()) { + names[limit.ordinal()] = null; + values[limit.ordinal()] = 0; + caches[limit.ordinal()] = null; + totalValue[limit.ordinal()] = 0; + } + } + + public void debugPrint(XMLSecurityManager securityManager) { + Formatter formatter = new Formatter(); + System.out.println(formatter.format("%30s %15s %15s %15s %30s", + "Property","Limit","Total size","Size","Entity Name")); + + for (Limit limit : Limit.values()) { + formatter = new Formatter(); + System.out.println(formatter.format("%30s %15d %15d %15d %30s", + limit.name(), + securityManager.getLimit(limit), + totalValue[limit.ordinal()], + values[limit.ordinal()], + names[limit.ordinal()])); + } + } +} diff --git a/jaxp/src/com/sun/org/apache/xalan/internal/utils/XMLSecurityManager.java b/jaxp/src/jdk/xml/internal/XMLSecurityManager.java similarity index 67% rename from jaxp/src/com/sun/org/apache/xalan/internal/utils/XMLSecurityManager.java rename to jaxp/src/jdk/xml/internal/XMLSecurityManager.java index 96bb46c8c91..2dd165757fd 100644 --- a/jaxp/src/com/sun/org/apache/xalan/internal/utils/XMLSecurityManager.java +++ b/jaxp/src/jdk/xml/internal/XMLSecurityManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2022, 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,19 +23,17 @@ * questions. */ -package com.sun.org.apache.xalan.internal.utils; +package jdk.xml.internal; -import com.sun.org.apache.xalan.internal.XalanConstants; +import com.sun.org.apache.xerces.internal.util.SecurityManager; import java.util.concurrent.CopyOnWriteArrayList; +import com.sun.org.apache.xalan.internal.XalanConstants; import org.xml.sax.SAXException; /** - * This class is not the same as that in Xerces. It is used to manage the - * state of corresponding Xerces properties and pass the values over to - * the Xerces Security Manager. * - * @author Joe Wang Oracle Corp. + * This class manages standard and implementation-specific limitations. * */ public final class XMLSecurityManager { @@ -66,38 +64,47 @@ String literal() { * Limits managed by the security manager */ public static enum Limit { - ENTITY_EXPANSION_LIMIT("EntityExpansionLimit", XalanConstants.JDK_ENTITY_EXPANSION_LIMIT, - XalanConstants.SP_ENTITY_EXPANSION_LIMIT, 0, 64000), + XalanConstants.SP_ENTITY_EXPANSION_LIMIT, 0, 64000, Processor.PARSER), MAX_OCCUR_NODE_LIMIT("MaxOccurLimit", XalanConstants.JDK_MAX_OCCUR_LIMIT, - XalanConstants.SP_MAX_OCCUR_LIMIT, 0, 5000), + XalanConstants.SP_MAX_OCCUR_LIMIT, 0, 5000, Processor.PARSER), ELEMENT_ATTRIBUTE_LIMIT("ElementAttributeLimit", XalanConstants.JDK_ELEMENT_ATTRIBUTE_LIMIT, - XalanConstants.SP_ELEMENT_ATTRIBUTE_LIMIT, 0, 10000), + XalanConstants.SP_ELEMENT_ATTRIBUTE_LIMIT, 0, 10000, Processor.PARSER), TOTAL_ENTITY_SIZE_LIMIT("TotalEntitySizeLimit", XalanConstants.JDK_TOTAL_ENTITY_SIZE_LIMIT, - XalanConstants.SP_TOTAL_ENTITY_SIZE_LIMIT, 0, 50000000), + XalanConstants.SP_TOTAL_ENTITY_SIZE_LIMIT, 0, 50000000, Processor.PARSER), GENERAL_ENTITY_SIZE_LIMIT("MaxEntitySizeLimit", XalanConstants.JDK_GENERAL_ENTITY_SIZE_LIMIT, - XalanConstants.SP_GENERAL_ENTITY_SIZE_LIMIT, 0, 0), + XalanConstants.SP_GENERAL_ENTITY_SIZE_LIMIT, 0, 0, Processor.PARSER), PARAMETER_ENTITY_SIZE_LIMIT("MaxEntitySizeLimit", XalanConstants.JDK_PARAMETER_ENTITY_SIZE_LIMIT, - XalanConstants.SP_PARAMETER_ENTITY_SIZE_LIMIT, 0, 1000000), + XalanConstants.SP_PARAMETER_ENTITY_SIZE_LIMIT, 0, 1000000, Processor.PARSER), MAX_ELEMENT_DEPTH_LIMIT("MaxElementDepthLimit", XalanConstants.JDK_MAX_ELEMENT_DEPTH, - XalanConstants.SP_MAX_ELEMENT_DEPTH, 0, 0), + XalanConstants.SP_MAX_ELEMENT_DEPTH, 0, 0, Processor.PARSER), MAX_NAME_LIMIT("MaxXMLNameLimit", XalanConstants.JDK_XML_NAME_LIMIT, - XalanConstants.SP_XML_NAME_LIMIT, 1000, 1000), + XalanConstants.SP_XML_NAME_LIMIT, 1000, 1000, Processor.PARSER), ENTITY_REPLACEMENT_LIMIT("EntityReplacementLimit", XalanConstants.JDK_ENTITY_REPLACEMENT_LIMIT, - XalanConstants.SP_ENTITY_REPLACEMENT_LIMIT, 0, 3000000); + XalanConstants.SP_ENTITY_REPLACEMENT_LIMIT, 0, 3000000, Processor.PARSER), + XPATH_GROUP_LIMIT("XPathGroupLimit", XalanConstants.XPATH_GROUP_LIMIT, + XalanConstants.XPATH_GROUP_LIMIT, 10, 10, Processor.XPATH), + XPATH_OP_LIMIT("XPathExprOpLimit", XalanConstants.XPATH_OP_LIMIT, + XalanConstants.XPATH_OP_LIMIT, 100, 100, Processor.XPATH), + XPATH_TOTALOP_LIMIT("XPathTotalOpLimit", XalanConstants.XPATH_TOTALOP_LIMIT, + XalanConstants.XPATH_TOTALOP_LIMIT, 10000, 10000, Processor.XPATH) + ; final String key; final String apiProperty; final String systemProperty; final int defaultValue; final int secureValue; + final Processor processor; - Limit(String key, String apiProperty, String systemProperty, int value, int secureValue) { + Limit(String key, String apiProperty, String systemProperty, int value, + int secureValue, Processor processor) { this.key = key; this.apiProperty = apiProperty; this.systemProperty = systemProperty; this.defaultValue = value; this.secureValue = secureValue; + this.processor = processor; } public boolean equalsAPIPropertyName(String propertyName) { @@ -124,6 +131,10 @@ public int defaultValue() { return defaultValue; } + public boolean isSupported(Processor p) { + return processor == p; + } + int secureValue() { return secureValue; } @@ -134,12 +145,9 @@ int secureValue() { */ public static enum NameMap { - ENTITY_EXPANSION_LIMIT(XalanConstants.SP_ENTITY_EXPANSION_LIMIT, - XalanConstants.ENTITY_EXPANSION_LIMIT), - MAX_OCCUR_NODE_LIMIT(XalanConstants.SP_MAX_OCCUR_LIMIT, - XalanConstants.MAX_OCCUR_LIMIT), - ELEMENT_ATTRIBUTE_LIMIT(XalanConstants.SP_ELEMENT_ATTRIBUTE_LIMIT, - XalanConstants.ELEMENT_ATTRIBUTE_LIMIT); + ENTITY_EXPANSION_LIMIT(XalanConstants.SP_ENTITY_EXPANSION_LIMIT, XalanConstants.ENTITY_EXPANSION_LIMIT), + MAX_OCCUR_NODE_LIMIT(XalanConstants.SP_MAX_OCCUR_LIMIT, XalanConstants.MAX_OCCUR_LIMIT), + ELEMENT_ATTRIBUTE_LIMIT(XalanConstants.SP_ELEMENT_ATTRIBUTE_LIMIT, XalanConstants.ELEMENT_ATTRIBUTE_LIMIT); final String newName; final String oldName; @@ -155,14 +163,32 @@ String getOldName(String newName) { return null; } } + + /** + * Supported processors + */ + public static enum Processor { + PARSER, + XPATH, + } + + private static final int NO_LIMIT = 0; + /** * Values of the properties */ private final int[] values; + /** * States of the settings for each property */ private State[] states; + + /** + * Flag indicating if secure processing is set + */ + boolean secureProcessing; + /** * States that determine if properties are set explicitly */ @@ -192,9 +218,10 @@ public XMLSecurityManager(boolean secureProcessing) { values = new int[Limit.values().length]; states = new State[Limit.values().length]; isSet = new boolean[Limit.values().length]; + this.secureProcessing = secureProcessing; for (Limit limit : Limit.values()) { if (secureProcessing) { - values[limit.ordinal()] = limit.secureValue(); + values[limit.ordinal()] = limit.secureValue; states[limit.ordinal()] = State.FSP; } else { values[limit.ordinal()] = limit.defaultValue(); @@ -209,6 +236,7 @@ public XMLSecurityManager(boolean secureProcessing) { * Setting FEATURE_SECURE_PROCESSING explicitly */ public void setSecureProcessing(boolean secure) { + secureProcessing = secure; for (Limit limit : Limit.values()) { if (secure) { setLimit(limit.ordinal(), State.FSP, limit.secureValue()); @@ -218,6 +246,14 @@ public void setSecureProcessing(boolean secure) { } } + /** + * Return the state of secure processing + * @return the state of secure processing + */ + public boolean isSecureProcessing() { + return secureProcessing; + } + /** * Set limit by property name and state * @param propertyName property name @@ -255,7 +291,6 @@ public void setLimit(Limit limit, State state, int value) { */ public void setLimit(int index, State state, Object value) { if (index == indexEntityCountInfo) { - //if it's explicitly set, it's treated as yes no matter the value printEntityCountInfo = (String)value; } else { int temp = 0; @@ -289,9 +324,8 @@ public void setLimit(int index, State state, int value) { } } - /** - * Return the value of the specified property. + * Return the value of the specified property * * @param propertyName the property name * @return the value of the property as a string. If a property is managed @@ -305,17 +339,6 @@ public String getLimitAsString(String propertyName) { return null; } - - /** - * Return the value of a property by its ordinal - * - * @param limit the property - * @return value of a property - */ - public String getLimitValueAsString(Limit limit) { - return Integer.toString(values[limit.ordinal()]); - } - /** * Return the value of the specified property * @@ -329,14 +352,15 @@ public int getLimit(Limit limit) { /** * Return the value of a property by its ordinal * - * @param index the index of a property + * @param limit the property * @return value of a property */ - public int getLimitByIndex(int index) { - return values[index]; + public String getLimitValueAsString(Limit limit) { + return Integer.toString(values[limit.ordinal()]); } + /** - * Return the value of a property by its index + * Return the value of a property by its ordinal * * @param index the index of a property * @return limit of a property as a string @@ -348,6 +372,7 @@ public String getLimitValueByIndex(int index) { return Integer.toString(values[index]); } + /** * Return the state of the limit property * @@ -388,6 +413,85 @@ public int getIndex(String propertyName) { return -1; } + /** + * Check if there's no limit defined by the Security Manager + * @param limit + * @return + */ + public boolean isNoLimit(int limit) { + return limit == NO_LIMIT; + } + /** + * Check if the size (length or count) of the specified limit property is + * over the limit + * + * @param limit the type of the limit property + * @param entityName the name of the entity + * @param size the size (count or length) of the entity + * @return true if the size is over the limit, false otherwise + */ + public boolean isOverLimit(Limit limit, String entityName, int size, + XMLLimitAnalyzer limitAnalyzer) { + return isOverLimit(limit.ordinal(), entityName, size, limitAnalyzer); + } + + /** + * Check if the value (length or count) of the specified limit property is + * over the limit + * + * @param index the index of the limit property + * @param entityName the name of the entity + * @param size the size (count or length) of the entity + * @return true if the size is over the limit, false otherwise + */ + public boolean isOverLimit(int index, String entityName, int size, + XMLLimitAnalyzer limitAnalyzer) { + if (values[index] == NO_LIMIT) { + return false; + } + if (size > values[index]) { + limitAnalyzer.addValue(index, entityName, size); + return true; + } + return false; + } + + /** + * Check against cumulated value + * + * @param limit the type of the limit property + * @param size the size (count or length) of the entity + * @return true if the size is over the limit, false otherwise + */ + public boolean isOverLimit(Limit limit, XMLLimitAnalyzer limitAnalyzer) { + return isOverLimit(limit.ordinal(), limitAnalyzer); + } + + public boolean isOverLimit(int index, XMLLimitAnalyzer limitAnalyzer) { + if (values[index] == NO_LIMIT) { + return false; + } + + if (index == Limit.ELEMENT_ATTRIBUTE_LIMIT.ordinal() || + index == Limit.ENTITY_EXPANSION_LIMIT.ordinal() || + index == Limit.TOTAL_ENTITY_SIZE_LIMIT.ordinal() || + index == Limit.ENTITY_REPLACEMENT_LIMIT.ordinal() || + index == Limit.MAX_ELEMENT_DEPTH_LIMIT.ordinal() || + index == Limit.MAX_NAME_LIMIT.ordinal() + ) { + return (limitAnalyzer.getTotalValue(index) > values[index]); + } else { + return (limitAnalyzer.getValue(index) > values[index]); + } + } + + public void debugPrint(XMLLimitAnalyzer limitAnalyzer) { + if (printEntityCountInfo.equals(XalanConstants.JDK_YES)) { + limitAnalyzer.debugPrint(this); + } + } + + /** * Indicate if a property is set explicitly * @param index @@ -400,6 +504,7 @@ public boolean isSet(int index) { public boolean printEntityCountInfo() { return printEntityCountInfo.equals(XalanConstants.JDK_YES); } + /** * Read from system properties, or those in jaxp.properties */ @@ -463,4 +568,37 @@ private boolean getSystemProperty(Limit limit, String sysPropertyName) { } return false; } + + + /** + * Convert a value set through setProperty to XMLSecurityManager. + * If the value is an instance of XMLSecurityManager, use it to override the default; + * If the value is an old SecurityManager, convert to the new XMLSecurityManager. + * + * @param value user specified security manager + * @param securityManager an instance of XMLSecurityManager + * @return an instance of the new security manager XMLSecurityManager + */ + public static XMLSecurityManager convert(Object value, XMLSecurityManager securityManager) { + if (value == null) { + if (securityManager == null) { + securityManager = new XMLSecurityManager(true); + } + return securityManager; + } + if (value instanceof XMLSecurityManager) { + return (XMLSecurityManager)value; + } else { + if (securityManager == null) { + securityManager = new XMLSecurityManager(true); + } + if (value instanceof SecurityManager) { + SecurityManager origSM = (SecurityManager)value; + securityManager.setLimit(Limit.MAX_OCCUR_NODE_LIMIT, State.APIPROPERTY, origSM.getMaxOccurNodeLimit()); + securityManager.setLimit(Limit.ENTITY_EXPANSION_LIMIT, State.APIPROPERTY, origSM.getEntityExpansionLimit()); + securityManager.setLimit(Limit.ELEMENT_ATTRIBUTE_LIMIT, State.APIPROPERTY, origSM.getElementAttrLimit()); + } + return securityManager; + } + } } diff --git a/jdk/src/macosx/classes/apple/security/KeychainStore.java b/jdk/src/macosx/classes/apple/security/KeychainStore.java index 034d6d43308..fcf2e8a771c 100644 --- a/jdk/src/macosx/classes/apple/security/KeychainStore.java +++ b/jdk/src/macosx/classes/apple/security/KeychainStore.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2022, 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 @@ -68,6 +68,25 @@ class TrustedCertEntry { Certificate cert; long certRef; // SecCertificateRef for this key + + // Each KeyStore.TrustedCertificateEntry have 2 attributes: + // 1. "trustSettings" -> trustSettings.toString() + // 2. "2.16.840.1.113894.746875.1.1" -> trustedKeyUsageValue + // The 1st one is mainly for debugging use. The 2nd one is similar + // to the attribute with the same key in a PKCS12KeyStore. + + // The SecTrustSettingsCopyTrustSettings() output for this certificate + // inside the KeyChain in its original array of CFDictionaryRef objects + // structure with values dumped as strings. For each trust, an extra + // entry "SecPolicyOid" is added whose value is the OID for this trust. + // The extra entries are used to construct trustedKeyUsageValue. + List> trustSettings; + + // One or more OIDs defined in http://oidref.com/1.2.840.113635.100.1. + // It can also be "2.5.29.37.0" for a self-signed certificate with + // an empty trust settings. This value is never empty. When there are + // multiple OID values, it takes the form of "[1.1.1, 1.1.2]". + String trustedKeyUsageValue; }; /** @@ -310,6 +329,69 @@ public Certificate engineGetCertificate(String alias) { } } + private final class LocalAttr + implements KeyStore.Entry.Attribute { + + private String name; + private String value; + + public LocalAttr(String name, String value) { + this.name = name; + this.value = value; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getValue() { + return value; + } + + /** + * Calculates a hash code value for the object. + * Objects that are equal will also have the same hashcode. + */ + public int hashCode() { + return Objects.hash(name, value); + } + + public boolean equals(Object obj) { + if (this == obj) return true; + + if (!(obj instanceof LocalAttr)) { + return false; + } + + LocalAttr other = + (LocalAttr) obj; + return (Objects.equals(name, other.getName()) && + Objects.equals(value, other.getValue())); + } + + } + + @Override + public KeyStore.Entry engineGetEntry(String alias, KeyStore.ProtectionParameter protParam) + throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableEntryException { + if (engineIsCertificateEntry(alias)) { + Object entry = entries.get(alias.toLowerCase()); + if (entry instanceof TrustedCertEntry) { + TrustedCertEntry tEntry = (TrustedCertEntry)entry; + Set attrs = new HashSet(){{ + add(new LocalAttr(KnownOIDs.ORACLE_TrustedKeyUsage.value(), tEntry.trustedKeyUsageValue)); + add(new LocalAttr("trustSettings", tEntry.trustSettings.toString())); + }}; + + return new KeyStore.TrustedCertificateEntry( + tEntry.cert, Collections.unmodifiableSet(attrs)); + } + } + return super.engineGetEntry(alias, protParam); + } + /** * Returns the creation date of the entry identified by the given alias. * @@ -463,55 +545,12 @@ public void engineSetKeyEntry(String alias, byte[] key, } /** - * Assigns the given certificate to the given alias. - * - *

If the given alias already exists in this keystore and identifies a - * trusted certificate entry, the certificate associated with it is - * overridden by the given certificate. - * - * @param alias the alias name - * @param cert the certificate - * - * @exception KeyStoreException if the given alias already exists and does - * not identify a trusted certificate entry, or this operation - * fails for some other reason. + * Adding trusted certificate entry is not supported. */ public void engineSetCertificateEntry(String alias, Certificate cert) - throws KeyStoreException - { - permissionCheck(); - - synchronized(entries) { - - Object entry = entries.get(alias.toLowerCase()); - if ((entry != null) && (entry instanceof KeyEntry)) { - throw new KeyStoreException - ("Cannot overwrite key entry with certificate"); - } - - // This will be slow, but necessary. Enumerate the values and then see if the cert matches the one in the trusted cert entry. - // Security framework doesn't support the same certificate twice in a keychain. - Collection allValues = entries.values(); - - for (Object value : allValues) { - if (value instanceof TrustedCertEntry) { - TrustedCertEntry tce = (TrustedCertEntry)value; - if (tce.cert.equals(cert)) { - throw new KeyStoreException("Keychain does not support mulitple copies of same certificate."); - } - } - } - - TrustedCertEntry trustedCertEntry = new TrustedCertEntry(); - trustedCertEntry.cert = cert; - trustedCertEntry.date = new Date(); - String lowerAlias = alias.toLowerCase(); - if (entries.get(lowerAlias) != null) { - deletedEntries.put(lowerAlias, entries.get(lowerAlias)); - } - entries.put(lowerAlias, trustedCertEntry); - addedEntries.put(lowerAlias, trustedCertEntry); - } + throws KeyStoreException { + throw new KeyStoreException("Cannot set trusted certificate entry." + + " Use the macOS \"security add-trusted-cert\" command instead."); } /** @@ -688,10 +727,7 @@ public void engineStore(OutputStream stream, char[] password) String alias = (String)e.nextElement(); Object entry = addedEntries.get(alias); if (entry instanceof TrustedCertEntry) { - TrustedCertEntry tce = (TrustedCertEntry)entry; - Certificate certElem; - certElem = tce.cert; - tce.certRef = addCertificateToKeychain(alias, certElem); + // Cannot set trusted certificate entry } else { KeyEntry keyEntry = (KeyEntry)entry; @@ -786,9 +822,28 @@ public void engineLoad(InputStream stream, char[] password) private native void _scanKeychain(); /** - * Callback method from _scanKeychain. If a trusted certificate is found, this method will be called. + * Callback method from _scanKeychain. If a trusted certificate is found, + * this method will be called. + * + * inputTrust is a list of strings in groups. Each group contains key/value + * pairs for one trust setting and ends with a null. Thus the size of the + * whole list is (2 * s_1 + 1) + (2 * s_2 + 1) + ... + (2 * s_n + 1), + * where s_i is the size of mapping for the i'th trust setting, + * and n is the number of trust settings. Ex: + * + * key1 for trust1 + * value1 for trust1 + * .. + * null (end of trust1) + * key1 for trust2 + * value1 for trust2 + * ... + * null (end of trust2) + * ... + * null (end if trust_n) */ - private void createTrustedCertEntry(String alias, long keychainItemRef, long creationDate, byte[] derStream) { + private void createTrustedCertEntry(String alias, List inputTrust, + long keychainItemRef, long creationDate, byte[] derStream) { TrustedCertEntry tce = new TrustedCertEntry(); try { @@ -799,6 +854,69 @@ private void createTrustedCertEntry(String alias, long keychainItemRef, long cre tce.cert = cert; tce.certRef = keychainItemRef; + tce.trustSettings = new ArrayList<>(); + Map tmpMap = new LinkedHashMap<>(); + for (int i = 0; i < inputTrust.size(); i++) { + if (inputTrust.get(i) == null) { + tce.trustSettings.add(tmpMap); + if (i < inputTrust.size() - 1) { + // Prepare an empty map for the next trust setting. + // Do not just clear(), must be a new object. + // Only create if not at end of list. + tmpMap = new LinkedHashMap<>(); + } + } else { + tmpMap.put(inputTrust.get(i), inputTrust.get(i+1)); + i++; + } + } + + boolean isSelfSigned; + try { + cert.verify(cert.getPublicKey()); + isSelfSigned = true; + } catch (Exception e) { + isSelfSigned = false; + } + if (tce.trustSettings.isEmpty()) { + if (isSelfSigned) { + // If a self-signed certificate has an empty trust settings, + // trust it for all purposes + tce.trustedKeyUsageValue = KnownOIDs.anyExtendedKeyUsage.value(); + } else { + // Otherwise, return immediately. The certificate is not + // added into entries. + return; + } + } else { + List values = new ArrayList<>(); + for (Map oneTrust : tce.trustSettings) { + String result = oneTrust.get("kSecTrustSettingsResult"); + // https://developer.apple.com/documentation/security/sectrustsettingsresult?language=objc + // 1 = kSecTrustSettingsResultTrustRoot, 2 = kSecTrustSettingsResultTrustAsRoot + // If missing, a default value of kSecTrustSettingsResultTrustRoot is assumed + // for self-signed certificates (see doc for SecTrustSettingsCopyTrustSettings). + // Note that the same SecPolicyOid can appear in multiple trust settings + // for different kSecTrustSettingsAllowedError and/or kSecTrustSettingsPolicyString. + if ((result == null && isSelfSigned) + || "1".equals(result) || "2".equals(result)) { + // When no kSecTrustSettingsPolicy, it means everything + String oid = oneTrust.getOrDefault("SecPolicyOid", + KnownOIDs.anyExtendedKeyUsage.value()); + if (!values.contains(oid)) { + values.add(oid); + } + } + } + if (values.isEmpty()) { + return; + } + if (values.size() == 1) { + tce.trustedKeyUsageValue = values.get(0); + } else { + tce.trustedKeyUsageValue = values.toString(); + } + } // Make a creation date. if (creationDate != 0) tce.date = new Date(creationDate); @@ -1151,4 +1269,3 @@ private byte[] encryptPrivateKey(byte[] data, char[] password) } - diff --git a/jdk/src/macosx/native/apple/security/KeystoreImpl.m b/jdk/src/macosx/native/apple/security/KeystoreImpl.m index 6a256c9118f..be4bd8cf68f 100644 --- a/jdk/src/macosx/native/apple/security/KeystoreImpl.m +++ b/jdk/src/macosx/native/apple/security/KeystoreImpl.m @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2022, 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,6 +24,7 @@ */ #import "apple_security_KeychainStore.h" +#import "jni_util.h" #import #import @@ -32,7 +33,7 @@ static JNF_CLASS_CACHE(jc_KeychainStore, "apple/security/KeychainStore"); -static JNF_MEMBER_CACHE(jm_createTrustedCertEntry, jc_KeychainStore, "createTrustedCertEntry", "(Ljava/lang/String;JJ[B)V"); +static JNF_MEMBER_CACHE(jm_createTrustedCertEntry, jc_KeychainStore, "createTrustedCertEntry", "(Ljava/lang/String;Ljava/util/List;JJ[B)V"); static JNF_MEMBER_CACHE(jm_createKeyEntry, jc_KeychainStore, "createKeyEntry", "(Ljava/lang/String;JJ[J[[B)V"); static jstring getLabelFromItem(JNIEnv *env, SecKeychainItemRef inItem) @@ -362,6 +363,14 @@ static void addIdentitiesToKeystore(JNIEnv *env, jobject keyStore) } } +#define ADD(list, str) { \ + jobject localeObj = (*env)->NewStringUTF(env, [str UTF8String]); \ + (*env)->CallBooleanMethod(env, list, jm_listAdd, localeObj); \ + (*env)->DeleteLocalRef(env, localeObj); \ +} + +#define ADDNULL(list) (*env)->CallBooleanMethod(env, list, jm_listAdd, NULL) + static void addCertificatesToKeystore(JNIEnv *env, jobject keyStore) { // Search the user keychain list for all X509 certificates. @@ -370,6 +379,13 @@ static void addCertificatesToKeystore(JNIEnv *env, jobject keyStore) SecKeychainItemRef theItem = NULL; OSErr searchResult = noErr; + jclass jc_arrayListClass = (*env)->FindClass(env, "java/util/ArrayList"); + CHECK_NULL(jc_arrayListClass); + jmethodID jm_arrayListCons = (*env)->GetMethodID(env, jc_arrayListClass, "", "()V"); + CHECK_NULL(jm_arrayListCons); + jmethodID jm_listAdd = (*env)->GetMethodID(env, jc_arrayListClass, "add", "(Ljava/lang/Object;)Z"); + CHECK_NULL(jm_listAdd); + do { searchResult = SecKeychainSearchCopyNext(keychainItemSearch, &theItem); @@ -390,12 +406,50 @@ static void addCertificatesToKeystore(JNIEnv *env, jobject keyStore) goto errOut; } + // Only add certificates with trusted settings + CFArrayRef trustSettings; + if (SecTrustSettingsCopyTrustSettings(certRef, kSecTrustSettingsDomainUser, &trustSettings) + == errSecItemNotFound) { + continue; + } + + // See KeychainStore::createTrustedCertEntry for content of inputTrust + jobject inputTrust = (*env)->NewObject(env, jc_arrayListClass, jm_arrayListCons); + CHECK_NULL(inputTrust); + + // Dump everything inside trustSettings into inputTrust + CFIndex count = CFArrayGetCount(trustSettings); + for (int i = 0; i < count; i++) { + CFDictionaryRef oneTrust = (CFDictionaryRef) CFArrayGetValueAtIndex(trustSettings, i); + CFIndex size = CFDictionaryGetCount(oneTrust); + const void * keys [size]; + const void * values [size]; + CFDictionaryGetKeysAndValues(oneTrust, keys, values); + for (int j = 0; j < size; j++) { + NSString* s = [NSString stringWithFormat:@"%@", keys[j]]; + ADD(inputTrust, s); + s = [NSString stringWithFormat:@"%@", values[j]]; + ADD(inputTrust, s); + } + SecPolicyRef certPolicy; + certPolicy = (SecPolicyRef)CFDictionaryGetValue(oneTrust, kSecTrustSettingsPolicy); + if (certPolicy != NULL) { + CFDictionaryRef policyDict = SecPolicyCopyProperties(certPolicy); + ADD(inputTrust, @"SecPolicyOid"); + NSString* s = [NSString stringWithFormat:@"%@", CFDictionaryGetValue(policyDict, @"SecPolicyOid")]; + ADD(inputTrust, s); + CFRelease(policyDict); + } + ADDNULL(inputTrust); + } + CFRelease(trustSettings); + // Find the creation date. jlong creationDate = getModDateFromItem(env, theItem); // Call back to the Java object to create Java objects corresponding to this security object. jlong nativeRef = ptr_to_jlong(certRef); - JNFCallVoidMethod(env, keyStore, jm_createTrustedCertEntry, alias, nativeRef, creationDate, certData); + JNFCallVoidMethod(env, keyStore, jm_createTrustedCertEntry, alias, inputTrust, nativeRef, creationDate, certData); } } while (searchResult == noErr); @@ -494,8 +548,8 @@ static void addCertificatesToKeystore(JNIEnv *env, jobject keyStore) /* * Class: apple_security_KeychainStore * Method: _addItemToKeychain - * Signature: (Ljava/lang/String;[B)I -*/ + * Signature: (Ljava/lang/String;Z[B[C)J + */ JNIEXPORT jlong JNICALL Java_apple_security_KeychainStore__1addItemToKeychain (JNIEnv *env, jobject this, jstring alias, jboolean isCertificate, jbyteArray rawDataObj, jcharArray passwordObj) { diff --git a/jdk/src/share/classes/com/sun/imageio/plugins/bmp/BMPImageReader.java b/jdk/src/share/classes/com/sun/imageio/plugins/bmp/BMPImageReader.java index ea6e1cdbdcd..49a817d3dd9 100644 --- a/jdk/src/share/classes/com/sun/imageio/plugins/bmp/BMPImageReader.java +++ b/jdk/src/share/classes/com/sun/imageio/plugins/bmp/BMPImageReader.java @@ -69,8 +69,9 @@ import java.util.List; import java.util.StringTokenizer; -import com.sun.imageio.plugins.common.ImageUtil; import com.sun.imageio.plugins.common.I18N; +import com.sun.imageio.plugins.common.ImageUtil; +import com.sun.imageio.plugins.common.ReaderUtil; /** This class is the Java Image IO plugin reader for BMP images. * It may subsample the image, clip the image, select sub-bands, @@ -1446,9 +1447,8 @@ private void readRLE8(byte bdata[]) throws IOException { } // Read till we have the whole image - byte values[] = new byte[imSize]; - int bytesRead = 0; - iis.readFully(values, 0, imSize); + byte[] values = ReaderUtil. + staggeredReadByteStream(iis, imSize); // Since data is compressed, decompress it decodeRLE8(imSize, padding, values, bdata); @@ -1567,8 +1567,8 @@ private void readRLE4(byte[] bdata) throws IOException { } // Read till we have the whole image - byte[] values = new byte[imSize]; - iis.readFully(values, 0, imSize); + byte[] values = ReaderUtil. + staggeredReadByteStream(iis, imSize); // Decompress the RLE4 compressed data. decodeRLE4(imSize, padding, values, bdata); diff --git a/jdk/src/share/classes/com/sun/imageio/plugins/common/ReaderUtil.java b/jdk/src/share/classes/com/sun/imageio/plugins/common/ReaderUtil.java index cedc428c3b4..42378cfa33d 100644 --- a/jdk/src/share/classes/com/sun/imageio/plugins/common/ReaderUtil.java +++ b/jdk/src/share/classes/com/sun/imageio/plugins/common/ReaderUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2022, 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 @@ -28,6 +28,8 @@ import java.awt.Point; import java.awt.Rectangle; import java.io.IOException; +import java.util.List; +import java.util.ArrayList; import javax.imageio.stream.ImageInputStream; /** @@ -213,4 +215,47 @@ public static int readMultiByteInteger(ImageInputStream iis) } return result; } + + /** + * An utility method to allocate and initialize a byte array + * step by step with pre-defined limit, instead of allocating + * a large array up-front based on the length derived from + * an image header. + * + * @param iis a {@code ImageInputStream} to decode data and store + * it in byte array. + * @param length the size of data to decode + * + * @return array of size length when decode succeeeds + * + * @throws IOException if decoding of stream fails + */ + public static byte[] staggeredReadByteStream(ImageInputStream iis, + int length) throws IOException { + final int UNIT_SIZE = 1024000; + byte[] decodedData; + if (length < UNIT_SIZE) { + decodedData = new byte[length]; + iis.readFully(decodedData, 0, length); + } else { + int bytesToRead = length; + int bytesRead = 0; + List bufs = new ArrayList<>(); + while (bytesToRead != 0) { + int sz = Math.min(bytesToRead, UNIT_SIZE); + byte[] unit = new byte[sz]; + iis.readFully(unit, 0, sz); + bufs.add(unit); + bytesRead += sz; + bytesToRead -= sz; + } + decodedData = new byte[bytesRead]; + int copiedBytes = 0; + for (byte[] ba : bufs) { + System.arraycopy(ba, 0, decodedData, copiedBytes, ba.length); + copiedBytes += ba.length; + } + } + return decodedData; + } } diff --git a/jdk/src/share/classes/com/sun/jndi/dns/DnsUrl.java b/jdk/src/share/classes/com/sun/jndi/dns/DnsUrl.java index ef17259695c..fbea3b4b17f 100644 --- a/jdk/src/share/classes/com/sun/jndi/dns/DnsUrl.java +++ b/jdk/src/share/classes/com/sun/jndi/dns/DnsUrl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2002, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2022, 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 @@ -27,7 +27,12 @@ import java.net.MalformedURLException; -import java.util.Hashtable; +import java.net.URI; +import java.net.URISyntaxException; +import java.security.AccessController; +import java.security.PrivilegedAction; +import sun.security.action.GetPropertyAction; +import java.util.Locale; import java.util.StringTokenizer; import com.sun.jndi.toolkit.url.Uri; @@ -56,6 +61,23 @@ public class DnsUrl extends Uri { + private static final String PARSE_MODE_PROP = "com.sun.jndi.dnsURLParsing"; + private static final ParseMode DEFAULT_PARSE_MODE = ParseMode.COMPAT; + + public static final ParseMode PARSE_MODE; + static { + PrivilegedAction action = + new GetPropertyAction(PARSE_MODE_PROP, DEFAULT_PARSE_MODE.toString()); + ParseMode parseMode = DEFAULT_PARSE_MODE; + try { + String mode = AccessController.doPrivileged(action); + parseMode = ParseMode.valueOf(mode.toUpperCase(Locale.ROOT)); + } catch (Throwable t) { + parseMode = DEFAULT_PARSE_MODE; + } finally { + PARSE_MODE = parseMode; + } + } private String domain; // domain name of the context @@ -71,19 +93,58 @@ public static DnsUrl[] fromList(String urlList) StringTokenizer st = new StringTokenizer(urlList, " "); while (st.hasMoreTokens()) { - urls[i++] = new DnsUrl(st.nextToken()); + try { + urls[i++] = new DnsUrl(validateURI(st.nextToken())); + } catch (URISyntaxException e) { + MalformedURLException mue = new MalformedURLException(e.getMessage()); + mue.initCause(e); + throw mue; + } } DnsUrl[] trimmed = new DnsUrl[i]; System.arraycopy(urls, 0, trimmed, 0, i); return trimmed; } + @Override + protected ParseMode parseMode() { + return PARSE_MODE; + } + + @Override + protected final boolean isSchemeOnly(String uri) { + return isDnsSchemeOnly(uri); + } + + @Override + protected boolean checkSchemeOnly(String uri, String scheme) { + return uri.equals(scheme + ":") || uri.equals(scheme + "://"); + } + + @Override + protected final MalformedURLException newInvalidURISchemeException(String uri) { + return new MalformedURLException( + uri + " is not a valid DNS pseudo-URL"); + } + + private static boolean isDnsSchemeOnly(String uri) { + return "dns:".equals(uri) || "dns://".equals(uri); + } + + private static String validateURI(String uri) throws URISyntaxException { + // no validation in legacy parsing mode + if (PARSE_MODE == ParseMode.LEGACY) return uri; + // special case of scheme-only URIs + if (isDnsSchemeOnly(uri)) return uri; + // use java.net.URI to validate the uri syntax + return new URI(uri).toString(); + } + public DnsUrl(String url) throws MalformedURLException { super(url); if (!scheme.equals("dns")) { - throw new MalformedURLException( - url + " is not a valid DNS pseudo-URL"); + throw newInvalidURISchemeException(url); } domain = path.startsWith("/") diff --git a/jdk/src/share/classes/com/sun/jndi/ldap/LdapURL.java b/jdk/src/share/classes/com/sun/jndi/ldap/LdapURL.java index 96365a13945..74802f3a8ac 100644 --- a/jdk/src/share/classes/com/sun/jndi/ldap/LdapURL.java +++ b/jdk/src/share/classes/com/sun/jndi/ldap/LdapURL.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2002, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2022, 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 @@ -28,6 +28,11 @@ import javax.naming.*; import java.net.MalformedURLException; import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.security.AccessController; +import java.security.PrivilegedAction; +import sun.security.action.GetPropertyAction; +import java.util.Locale; import java.util.StringTokenizer; import com.sun.jndi.toolkit.url.Uri; import com.sun.jndi.toolkit.url.UrlUtil; @@ -64,6 +69,24 @@ final public class LdapURL extends Uri { + private static final String PARSE_MODE_PROP = "com.sun.jndi.ldapURLParsing"; + private static final ParseMode DEFAULT_PARSE_MODE = ParseMode.COMPAT; + + public static final ParseMode PARSE_MODE; + static { + PrivilegedAction action = + new GetPropertyAction(PARSE_MODE_PROP, DEFAULT_PARSE_MODE.toString()); + ParseMode parseMode = DEFAULT_PARSE_MODE; + try { + String mode = AccessController.doPrivileged(action); + parseMode = ParseMode.valueOf(mode.toUpperCase(Locale.ROOT)); + } catch (Throwable t) { + parseMode = DEFAULT_PARSE_MODE; + } finally { + PARSE_MODE = parseMode; + } + } + private boolean useSsl = false; private String DN = null; private String attributes = null; @@ -83,7 +106,7 @@ public LdapURL(String url) throws NamingException { useSsl = scheme.equalsIgnoreCase("ldaps"); if (! (scheme.equalsIgnoreCase("ldap") || useSsl)) { - throw new MalformedURLException("Not an LDAP URL: " + url); + throw newInvalidURISchemeException(url); } parsePathAndQuery(); // DN, attributes, scope, filter, extensions @@ -99,6 +122,21 @@ public LdapURL(String url) throws NamingException { } } + @Override + protected MalformedURLException newInvalidURISchemeException(String uri) { + return new MalformedURLException("Not an LDAP URL: " + uri); + } + + @Override + protected boolean isSchemeOnly(String uri) { + return isLdapSchemeOnly(uri); + } + + @Override + protected ParseMode parseMode() { + return PARSE_MODE; + } + /** * Returns true if the URL is an LDAPS URL. */ @@ -151,13 +189,33 @@ public static String[] fromList(String urlList) throws NamingException { StringTokenizer st = new StringTokenizer(urlList, " "); while (st.hasMoreTokens()) { - urls[i++] = st.nextToken(); + // we don't accept scheme-only URLs here + urls[i++] = validateURI(st.nextToken()); } String[] trimmed = new String[i]; System.arraycopy(urls, 0, trimmed, 0, i); return trimmed; } + public static boolean isLdapSchemeOnly(String uri) { + return "ldap:".equals(uri) || "ldaps:".equals(uri); + } + + public static String validateURI(String uri) { + // no validation in legacy mode parsing + if (PARSE_MODE == ParseMode.LEGACY) { + return uri; + } + + // special case of scheme-only URIs + if (isLdapSchemeOnly(uri)) { + return uri; + } + + // use java.net.URI to validate the uri syntax + return URI.create(uri).toString(); + } + /** * Derermines whether an LDAP URL has query components. */ @@ -181,7 +239,8 @@ static String toUrlString(String host, int port, String dn, boolean useSsl) String p = (port != -1) ? (":" + port) : ""; String d = (dn != null) ? ("/" + UrlUtil.encode(dn, "UTF8")) : ""; - return useSsl ? "ldaps://" + h + p + d : "ldap://" + h + p + d; + String uri = useSsl ? "ldaps://" + h + p + d : "ldap://" + h + p + d; + return validateURI(uri); } catch (UnsupportedEncodingException e) { // UTF8 should always be supported throw new IllegalStateException("UTF-8 encoding unavailable"); diff --git a/jdk/src/share/classes/com/sun/jndi/toolkit/url/GenericURLContext.java b/jdk/src/share/classes/com/sun/jndi/toolkit/url/GenericURLContext.java index 3e90b27bcce..8c1daa5b4c7 100644 --- a/jdk/src/share/classes/com/sun/jndi/toolkit/url/GenericURLContext.java +++ b/jdk/src/share/classes/com/sun/jndi/toolkit/url/GenericURLContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2022, 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 @@ -32,6 +32,8 @@ import java.util.Hashtable; import java.net.MalformedURLException; +import com.sun.jndi.toolkit.url.Uri.ParseMode; + /** * This abstract class is a generic URL context that accepts as the * name argument either a string URL or a Name whose first component @@ -48,6 +50,7 @@ * @author Rosanna Lee */ abstract public class GenericURLContext implements Context { + protected Hashtable myEnv = null; @SuppressWarnings("unchecked") // Expect Hashtable @@ -159,8 +162,18 @@ protected String getURLPrefix(String url) throws NamingException { if (url.startsWith("//", start)) { start += 2; // skip double slash - // find last slash - int posn = url.indexOf("/", start); + // find where the authority component ends + // and the rest of the URL starts + int slash = url.indexOf('/', start); + int qmark = url.indexOf('?', start); + int fmark = url.indexOf('#', start); + if (fmark > -1 && qmark > fmark) qmark = -1; + if (fmark > -1 && slash > fmark) slash = -1; + if (qmark > -1 && slash > qmark) slash = -1; + int posn = slash > -1 ? slash + : (qmark > -1 ? qmark + : (fmark > -1 ? fmark + : url.length())); if (posn >= 0) { start = posn; } else { diff --git a/jdk/src/share/classes/com/sun/jndi/toolkit/url/Uri.java b/jdk/src/share/classes/com/sun/jndi/toolkit/url/Uri.java index da5771ae318..64da9015295 100644 --- a/jdk/src/share/classes/com/sun/jndi/toolkit/url/Uri.java +++ b/jdk/src/share/classes/com/sun/jndi/toolkit/url/Uri.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2001, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2022, 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 @@ -27,6 +27,8 @@ import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; /** @@ -36,15 +38,17 @@ * *

The java.net.URL class cannot be used to parse URIs since it * requires the installation of URL stream handlers that may not be - * available. The hack of getting around this by temporarily - * replacing the scheme part of a URI is not appropriate here: JNDI - * service providers must work on older Java platforms, and we want - * new features and bug fixes that are not available in old versions - * of the URL class. + * available. * - *

It may be appropriate to drop this code in favor of the - * java.net.URI class. The changes would need to be written so as to - * still run on pre-1.4 platforms not containing that class. + *

The {@linkplain ParseMode#STRICT strict} parsing mode uses + * the java.net.URI class to syntactically validate URI strings. + * The {@linkplain ParseMode#COMPAT compat} mode validate the + * URI authority and rejects URI fragments, but doesn't perform any + * additional validation on path and query, other than that + * which may be implemented in the concrete the Uri subclasses. + * The {@linkplain ParseMode#LEGACY legacy} mode should not be + * used unless the application is capable of validating all URI + * strings before any constructors of this class is invoked. * *

The format of an absolute URI (see the RFCs mentioned above) is: *

@@ -105,6 +109,28 @@
 
 public class Uri {
 
+    // three parsing modes
+    public enum ParseMode {
+        /**
+         * Strict validation mode.
+         * Validate the URI syntactically using {@link java.net.URI}.
+         * Rejects URI fragments unless explicitly supported by the
+         * subclass.
+         */
+        STRICT,
+        /**
+         * Compatibility mode. The URI authority is syntactically validated.
+         * Rejects URI fragments unless explicitly supported by the
+         * subclass.
+         * This is the default.
+         */
+        COMPAT,
+        /**
+         * Legacy mode. In this mode, no validation is performed.
+         */
+        LEGACY
+     }
+
     protected String uri;
     protected String scheme;
     protected String host = null;
@@ -112,6 +138,7 @@ public class Uri {
     protected boolean hasAuthority;
     protected String path;
     protected String query = null;
+    protected String fragment;
 
 
     /**
@@ -128,6 +155,15 @@ public Uri(String uri) throws MalformedURLException {
     protected Uri() {
     }
 
+    /**
+     * The parse mode for parsing this URI.
+     * The default is {@link ParseMode#COMPAT}.
+     * @return the parse mode for parsing this URI.
+     */
+    protected ParseMode parseMode() {
+        return ParseMode.COMPAT;
+    }
+
     /**
      * Initializes a Uri object given a URI string.
      * This method must be called exactly once, and before any other Uri
@@ -135,7 +171,7 @@ protected Uri() {
      */
     protected void init(String uri) throws MalformedURLException {
         this.uri = uri;
-        parse(uri);
+        parse(uri, parseMode());
     }
 
     /**
@@ -188,10 +224,235 @@ public String toString() {
         return uri;
     }
 
+    private void parse(String uri, ParseMode mode) throws MalformedURLException {
+        switch (mode) {
+            case STRICT:
+                parseStrict(uri);
+                break;
+            case COMPAT:
+                parseCompat(uri);
+                break;
+            case LEGACY:
+                parseLegacy(uri);
+                break;
+        }
+    }
+
+    /*
+     * Parses a URI string and sets this object's fields accordingly.
+     * Use java.net.URI to validate the uri string syntax
+     */
+    private void parseStrict(String uri) throws MalformedURLException {
+        try {
+            if (!isSchemeOnly(uri)) {
+                URI u = new URI(uri);
+                scheme = u.getScheme();
+                if (scheme == null) throw new MalformedURLException("Invalid URI: " + uri);
+                String auth = u.getRawAuthority();
+                hasAuthority = auth != null;
+                if (hasAuthority) {
+                    String host = u.getHost();
+                    int port = u.getPort();
+                    if (host != null) this.host = host;
+                    if (port != -1) this.port = port;
+                    String hostport = (host == null ? "" : host)
+                            + (port == -1 ? "" : (":" + port));
+                    if (!hostport.equals(auth)) {
+                        // throw if we have user info or regname
+                        throw new MalformedURLException("unsupported authority: " + auth);
+                    }
+                }
+                path = u.getRawPath();
+                if (u.getRawQuery() != null) {
+                    query = "?" + u.getRawQuery();
+                }
+                if (u.getRawFragment() != null) {
+                    if (!acceptsFragment()) {
+                        throw new MalformedURLException("URI fragments not supported: " + uri);
+                    }
+                    fragment = "#" + u.getRawFragment();
+                }
+            } else {
+                // scheme-only URIs are not supported by java.net.URI
+                // validate the URI by appending "/" to the uri string.
+                String s = uri.substring(0, uri.indexOf(':'));
+                URI u = new URI(uri + "/");
+                if (!s.equals(u.getScheme())
+                        || !checkSchemeOnly(uri, u.getScheme())) {
+                    throw newInvalidURISchemeException(uri);
+                }
+                scheme = s;
+                path = "";
+            }
+        } catch (URISyntaxException e) {
+            MalformedURLException mue =  new MalformedURLException(e.getMessage());
+            mue.initCause(e);
+            throw mue;
+        }
+    }
+
+
+    /*
+     * Parses a URI string and sets this object's fields accordingly.
+     * Compatibility mode. Use java.net.URI to validate the syntax of
+     * the uri string authority.
+     */
+    private void parseCompat(String uri) throws MalformedURLException {
+        int i;  // index into URI
+
+        i = uri.indexOf(':');                           // parse scheme
+        int slash = uri.indexOf('/');
+        int qmark = uri.indexOf('?');
+        int fmark = uri.indexOf('#');
+        if (i < 0 || slash > 0 && i > slash || qmark > 0 && i > qmark || fmark > 0 && i > fmark) {
+            throw new MalformedURLException("Invalid URI: " + uri);
+        }
+        if (fmark > -1) {
+            if (!acceptsFragment()) {
+                throw new MalformedURLException("URI fragments not supported: " + uri);
+            }
+        }
+        if (i == uri.length() - 1) {
+            if (!isSchemeOnly(uri)) {
+                throw newInvalidURISchemeException(uri);
+            }
+        }
+        scheme = uri.substring(0, i);
+        i++;                                            // skip past ":"
+
+        hasAuthority = uri.startsWith("//", i);
+        if (fmark > -1 && qmark > fmark) qmark = -1;
+        int endp = qmark > -1 ? qmark : fmark > -1 ? fmark : uri.length();
+        if (hasAuthority) {                             // parse "//host:port"
+            i += 2;                                     // skip past "//"
+            int starta = i;
+            // authority ends at the first appearance of /, ?, or #
+            int enda = uri.indexOf('/', i);
+            if (enda == -1 || qmark > -1 && qmark < enda) enda = qmark;
+            if (enda == -1 || fmark > -1 && fmark < enda) enda = fmark;
+            if (enda < 0) {
+                enda = uri.length();
+            }
+            if (uri.startsWith(":", i)) {
+                // LdapURL supports empty host.
+                i++;
+                host = "";
+                if (enda > i) {
+                    port = Integer.parseInt(uri.substring(i, enda));
+                }
+            } else {
+                // Use URI to parse authority
+                try {
+                    // URI requires at least one char after authority:
+                    // we use "/" and expect that the resulting URI path
+                    // will be exactly "/".
+                    URI u = new URI(uri.substring(0, enda) + "/");
+                    String auth = uri.substring(starta, enda);
+                    host = u.getHost();
+                    port = u.getPort();
+                    String p = u.getRawPath();
+                    String q = u.getRawQuery();
+                    String f = u.getRawFragment();
+                    String ui = u.getRawUserInfo();
+                    if (ui != null) {
+                        throw new MalformedURLException("user info not supported in authority: " + ui);
+                    }
+                    if (!"/".equals(p)) {
+                        throw new MalformedURLException("invalid authority: " + auth);
+                    }
+                    if (q != null) {
+                        throw new MalformedURLException("invalid trailing characters in authority: ?" + q);
+                    }
+                    if (f != null) {
+                        throw new MalformedURLException("invalid trailing characters in authority: #" + f);
+                    }
+                    String hostport = (host == null ? "" : host)
+                            + (port == -1?"":(":" + port));
+                    if (!auth.equals(hostport)) {
+                        // throw if we have user info or regname
+                        throw new MalformedURLException("unsupported authority: " + auth);
+                    }
+                } catch (URISyntaxException e) {
+                    MalformedURLException mue = new MalformedURLException(e.getMessage());
+                    mue.initCause(e);
+                    throw mue;
+                }
+            }
+            i = enda;
+        }
+        path = uri.substring(i, endp);
+        // look for query
+        if (qmark > -1) {
+            if (fmark > -1) {
+                query = uri.substring(qmark, fmark);
+            } else {
+                query = uri.substring(qmark);
+            }
+        }
+        if (fmark > -1) {
+            fragment = uri.substring(fmark);
+        }
+    }
+
+    /**
+     * A subclass of {@code Uri} that supports scheme only
+     * URIs can override this method and return true in the
+     * case where the URI string is a scheme-only URI that
+     * the subclass supports.
+     * @implSpec
+     * The default implementation of this method returns false,
+     * always.
+     * @param uri An URI string
+     * @return if this is a scheme-only URI supported by the subclass
+     */
+    protected boolean isSchemeOnly(String uri) {
+        return false;
+    }
+
+    /**
+     * Checks whether the given uri string should be considered
+     * as a scheme-only URI. For some protocols - e.g. DNS, we
+     * might accept "dns://" as a valid URL denoting default DNS.
+     * For others - we might only accept "scheme:".
+     * @implSpec
+     * The default implementation of this method returns true if
+     * the URI is of the form {@code ":"} with nothing
+     * after the scheme delimiter.
+     * @param uri the URI
+     * @param scheme the scheme
+     * @return true if the URI should be considered as a scheme-only
+     *         URI supported by this URI scheme.
+     */
+    protected boolean checkSchemeOnly(String uri, String scheme) {
+        return uri.equals(scheme + ":");
+    }
+
+    /**
+     * Creates a {@code MalformedURLException} to be thrown when the
+     * URI scheme is not supported.
+     *
+     * @param uri the URI string
+     * @return a {@link MalformedURLException}
+     */
+    protected MalformedURLException newInvalidURISchemeException(String uri) {
+        return new MalformedURLException("Invalid URI scheme: " + uri);
+    }
+
+    /**
+     * Whether fragments are supported.
+     * @implSpec
+     * The default implementation of this method retturns false, always.
+     * @return true if fragments are supported.
+     */
+    protected boolean acceptsFragment() {
+        return parseMode() == ParseMode.LEGACY;
+    }
+
     /*
      * Parses a URI string and sets this object's fields accordingly.
+     * Legacy parsing mode.
      */
-    private void parse(String uri) throws MalformedURLException {
+    private void parseLegacy(String uri) throws MalformedURLException {
         int i;  // index into URI
 
         i = uri.indexOf(':');                           // parse scheme
diff --git a/jdk/src/share/classes/com/sun/jndi/url/rmi/rmiURLContext.java b/jdk/src/share/classes/com/sun/jndi/url/rmi/rmiURLContext.java
index 4c6c78110fb..8f1732f7efd 100644
--- a/jdk/src/share/classes/com/sun/jndi/url/rmi/rmiURLContext.java
+++ b/jdk/src/share/classes/com/sun/jndi/url/rmi/rmiURLContext.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2022, 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
@@ -25,12 +25,18 @@
 
 package com.sun.jndi.url.rmi;
 
+import java.net.URI;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import sun.security.action.GetPropertyAction;
 import java.util.Hashtable;
+import java.util.Locale;
 
 import javax.naming.*;
 import javax.naming.spi.ResolveResult;
 import com.sun.jndi.toolkit.url.GenericURLContext;
 import com.sun.jndi.rmi.registry.RegistryContext;
+import com.sun.jndi.toolkit.url.Uri.ParseMode;
 
 
 /**
@@ -47,75 +53,268 @@
  */
 public class rmiURLContext extends GenericURLContext {
 
-    public rmiURLContext(Hashtable env) {
-        super(env);
-    }
+    private static final String PARSE_MODE_PROP = "com.sun.jndi.rmiURLParsing";
+    private static final ParseMode DEFAULT_PARSE_MODE = ParseMode.COMPAT;
 
-    /**
-     * Resolves the registry portion of "url" to the corresponding
-     * RMI registry, and returns the atomic object name as the
-     * remaining name.
-     */
-    protected ResolveResult getRootURLContext(String url, Hashtable env)
-            throws NamingException
-    {
-        if (!url.startsWith("rmi:")) {
-            throw (new IllegalArgumentException(
-                    "rmiURLContext: name is not an RMI URL: " + url));
+    public static final ParseMode PARSE_MODE;
+    static {
+        PrivilegedAction action =
+                new GetPropertyAction(PARSE_MODE_PROP, DEFAULT_PARSE_MODE.toString());
+        ParseMode parseMode = DEFAULT_PARSE_MODE;
+        try {
+            String mode = AccessController.doPrivileged(action);
+            parseMode = ParseMode.valueOf(mode.toUpperCase(Locale.ROOT));
+        } catch (Throwable t) {
+            parseMode = DEFAULT_PARSE_MODE;
+        } finally {
+            PARSE_MODE = parseMode;
         }
+    }
 
-        // Parse the URL.
+    public rmiURLContext(Hashtable env) {
+        super(env);
+    }
 
+    public static class Parser {
+        final String url;
+        final ParseMode mode;
         String host = null;
         int port = -1;
         String objName = null;
+        public Parser(String url) {
+            this(url, PARSE_MODE);
+        }
+        public Parser(String url, ParseMode mode) {
+            this.url = url;
+            this.mode = mode;
+        }
 
-        int i = 4;              // index into url, following the "rmi:"
+        public String url() {return url;}
+        public String host() {return host;}
+        public int port() {return port;}
+        public String objName() {return objName;}
+        public ParseMode mode() {return mode;}
 
-        if (url.startsWith("//", i)) {          // parse "//host:port"
-            i += 2;                             // skip past "//"
-            int slash = url.indexOf('/', i);
-            if (slash < 0) {
-                slash = url.length();
-            }
-            if (url.startsWith("[", i)) {               // at IPv6 literal
-                int brac = url.indexOf(']', i + 1);
-                if (brac < 0 || brac > slash) {
-                    throw new IllegalArgumentException(
-                        "rmiURLContext: name is an Invalid URL: " + url);
+        public void parse() throws NamingException {
+            if (!url.startsWith("rmi:")) {
+                throw (new IllegalArgumentException(
+                        "rmiURLContext: name is not an RMI URL: " + url));
+            }
+
+            switch (mode) {
+                case STRICT:
+                    parseStrict();
+                    break;
+                case COMPAT:
+                    parseCompat();
+                    break;
+                case LEGACY:
+                    parseLegacy();
+                    break;
+            }
+
+        }
+
+        private void parseStrict() throws NamingException {
+            assert url.startsWith("rmi:");
+
+            if (url.equals("rmi:") || url.equals("rmi://")) return;
+
+            // index into url, following the "rmi:"
+            int i = 4;
+
+            if (url.startsWith("//", i)) {
+                i += 2;
+                try {
+                    URI uri = URI.create(url);
+                    host = uri.getHost();
+                    port = uri.getPort();
+                    String auth = uri.getRawAuthority();
+                    String hostport = (host == null ? "" : host)
+                            + (port == -1 ? "" : ":" + port);
+                    if (!hostport.equals(auth)) {
+                        boolean failed = true;
+                        if (hostport.equals("") && auth.startsWith(":")) {
+                            // supports missing host
+                            try {
+                                port = Integer.parseInt(auth.substring(1));
+                                failed = false;
+                            } catch (NumberFormatException x) {
+                                failed = true;
+                            }
+                        }
+                        if (failed) {
+                            throw newNamingException(new IllegalArgumentException("invalid authority: "
+                                    + auth));
+                        }
+                    }
+                    i += auth.length();
+                } catch (IllegalArgumentException iae) {
+                    throw newNamingException(iae);
+                }
+            }
+            int fmark = url.indexOf('#', i);
+            if (fmark > -1) {
+                if (!acceptsFragment()) {
+                    throw newNamingException(new IllegalArgumentException("URI fragments not supported: " + url));
                 }
-                host = url.substring(i, brac + 1);      // include brackets
-                i = brac + 1;                           // skip past "[...]"
-            } else {                                    // at host name or IPv4
-                int colon = url.indexOf(':', i);
-                int hostEnd = (colon < 0 || colon > slash)
-                    ? slash
-                    : colon;
-                if (i < hostEnd) {
-                    host = url.substring(i, hostEnd);
+            }
+
+            if ("".equals(host)) {
+                host = null;
+            }
+            if (url.startsWith("/", i)) {           // skip "/" before object name
+                i++;
+            }
+            if (i < url.length()) {
+                objName = url.substring(i);
+            }
+        }
+
+        private void parseCompat() throws NamingException {
+            assert url.startsWith("rmi:");
+
+            int i = 4;              // index into url, following the "rmi:"
+            boolean hasAuthority = url.startsWith("//", i);
+            if (hasAuthority) i += 2;  // skip past "//"
+            int slash = url.indexOf('/', i);
+            int qmark = url.indexOf('?', i);
+            int fmark = url.indexOf('#', i);
+            if (fmark > -1 && qmark > fmark) qmark = -1;
+            if (fmark > -1 && slash > fmark) slash = -1;
+            if (qmark > -1 && slash > qmark) slash = -1;
+
+            // The end of the authority component is either the
+            // slash (slash will be -1 if it doesn't come before
+            // query or fragment), or the question mark (qmark will
+            // be -1 if it doesn't come before the fragment), or
+            // the fragment separator, or the end of the URI
+            // string if there is no path, no query, and no fragment.
+            int enda = slash > -1 ? slash
+                    : (qmark > -1 ? qmark
+                    : (fmark > -1 ? fmark
+                    : url.length()));
+            if (fmark > -1) {
+                if (!acceptsFragment()) {
+                    throw newNamingException(new IllegalArgumentException("URI fragments not supported: " + url));
                 }
-                i = hostEnd;                            // skip past host
             }
-            if ((i + 1 < slash)) {
-                if ( url.startsWith(":", i)) {       // parse port
-                    i++;                             // skip past ":"
-                    port = Integer.parseInt(url.substring(i, slash));
+
+            if (hasAuthority && enda > i) {          // parse "//host:port"
+                if (url.startsWith(":", i)) {
+                    // LdapURL supports empty host.
+                    i++;
+                    host = "";
+                    if (enda > i) {
+                        port = Integer.parseInt(url.substring(i, enda));
+                    }
                 } else {
-                    throw new IllegalArgumentException(
-                        "rmiURLContext: name is an Invalid URL: " + url);
+                    try {
+                        URI uri = URI.create(url.substring(0, enda));
+                        host = uri.getHost();
+                        port = uri.getPort();
+                        String hostport = (host == null ? "" : host)
+                                + (port == -1 ? "" : ":" + port);
+                        if (!hostport.equals(uri.getRawAuthority())) {
+                            throw newNamingException(new IllegalArgumentException("invalid authority: "
+                                    + uri.getRawAuthority()));
+                        }
+                    } catch (IllegalArgumentException iae) {
+                        throw newNamingException(iae);
+                    }
                 }
+                i = enda;
+            }
+            if ("".equals(host)) {
+                host = null;
+            }
+            if (url.startsWith("/", i)) {           // skip "/" before object name
+                i++;
             }
-            i = slash;
+            if (i < url.length()) {
+                objName = url.substring(i);
+            }
+
         }
-        if ("".equals(host)) {
-            host = null;
+
+        // The legacy parsing used to only throw IllegalArgumentException
+        // and continues to do so
+        private void parseLegacy() {
+            assert url.startsWith("rmi:");
+
+            // Parse the URL.
+            int i = 4;              // index into url, following the "rmi:"
+
+            if (url.startsWith("//", i)) {          // parse "//host:port"
+                i += 2;                             // skip past "//"
+                int slash = url.indexOf('/', i);
+                if (slash < 0) {
+                    slash = url.length();
+                }
+                if (url.startsWith("[", i)) {               // at IPv6 literal
+                    int brac = url.indexOf(']', i + 1);
+                    if (brac < 0 || brac > slash) {
+                        throw new IllegalArgumentException(
+                                "rmiURLContext: name is an Invalid URL: " + url);
+                    }
+                    host = url.substring(i, brac + 1);      // include brackets
+                    i = brac + 1;                           // skip past "[...]"
+                } else {                                    // at host name or IPv4
+                    int colon = url.indexOf(':', i);
+                    int hostEnd = (colon < 0 || colon > slash)
+                            ? slash
+                            : colon;
+                    if (i < hostEnd) {
+                        host = url.substring(i, hostEnd);
+                    }
+                    i = hostEnd;                            // skip past host
+                }
+                if ((i + 1 < slash)) {
+                    if ( url.startsWith(":", i)) {       // parse port
+                        i++;                             // skip past ":"
+                        port = Integer.parseInt(url.substring(i, slash));
+                    } else {
+                        throw new IllegalArgumentException(
+                                "rmiURLContext: name is an Invalid URL: " + url);
+                    }
+                }
+                i = slash;
+            }
+            if ("".equals(host)) {
+                host = null;
+            }
+            if (url.startsWith("/", i)) {           // skip "/" before object name
+                i++;
+            }
+            if (i < url.length()) {
+                objName = url.substring(i);
+            }
         }
-        if (url.startsWith("/", i)) {           // skip "/" before object name
-            i++;
+
+        NamingException newNamingException(Throwable cause) {
+            NamingException ne = new InvalidNameException(cause.getMessage());
+            ne.initCause(cause);
+            return ne;
         }
-        if (i < url.length()) {
-            objName = url.substring(i);
+
+        protected boolean acceptsFragment() {
+            return true;
         }
+    }
+
+    /**
+     * Resolves the registry portion of "url" to the corresponding
+     * RMI registry, and returns the atomic object name as the
+     * remaining name.
+     */
+    protected ResolveResult getRootURLContext(String url, Hashtable env)
+            throws NamingException
+    {
+        Parser parser = new Parser(url);
+        parser.parse();
+        String host = parser.host;
+        int port = parser.port;
+        String objName = parser.objName;
 
         // Represent object name as empty or single-component composite name.
         CompositeName remaining = new CompositeName();
diff --git a/jdk/src/share/classes/com/sun/media/sound/AudioFileSoundbankReader.java b/jdk/src/share/classes/com/sun/media/sound/AudioFileSoundbankReader.java
index 11b0fa6ee6e..841de2f9b02 100644
--- a/jdk/src/share/classes/com/sun/media/sound/AudioFileSoundbankReader.java
+++ b/jdk/src/share/classes/com/sun/media/sound/AudioFileSoundbankReader.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2021, 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
@@ -76,12 +76,26 @@ public Soundbank getSoundbank(InputStream stream)
 
     public Soundbank getSoundbank(AudioInputStream ais)
             throws InvalidMidiDataException, IOException {
+        int MEGABYTE = 1048576;
+        int DEFAULT_BUFFER_SIZE = 65536;
+        int MAX_FRAME_SIZE = 1024;
         try {
             byte[] buffer;
-            if (ais.getFrameLength() == -1) {
+            int frameSize = ais.getFormat().getFrameSize();
+            if (frameSize <= 0 || frameSize > MAX_FRAME_SIZE) {
+                throw new InvalidMidiDataException("Formats with frame size "
+                        + frameSize + " are not supported");
+            }
+
+            long totalSize = ais.getFrameLength() * frameSize;
+            if (totalSize >= Integer.MAX_VALUE - 2) {
+                throw new InvalidMidiDataException(
+                        "Can not allocate enough memory to read audio data.");
+            }
+
+            if (ais.getFrameLength() == -1 || totalSize > MEGABYTE) {
                 ByteArrayOutputStream baos = new ByteArrayOutputStream();
-                byte[] buff = new byte[1024
-                        - (1024 % ais.getFormat().getFrameSize())];
+                byte[] buff = new byte[DEFAULT_BUFFER_SIZE - (DEFAULT_BUFFER_SIZE % frameSize)];
                 int ret;
                 while ((ret = ais.read(buff)) != -1) {
                     baos.write(buff, 0, ret);
@@ -89,8 +103,7 @@ public Soundbank getSoundbank(AudioInputStream ais)
                 ais.close();
                 buffer = baos.toByteArray();
             } else {
-                buffer = new byte[(int) (ais.getFrameLength()
-                                    * ais.getFormat().getFrameSize())];
+                buffer = new byte[(int) totalSize];
                 new DataInputStream(ais).readFully(buffer);
             }
             ModelByteBufferWavetable osc = new ModelByteBufferWavetable(
diff --git a/jdk/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/implementations/KeyInfoReferenceResolver.java b/jdk/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/implementations/KeyInfoReferenceResolver.java
index 15a73b7379f..65ee8653e5f 100644
--- a/jdk/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/implementations/KeyInfoReferenceResolver.java
+++ b/jdk/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/implementations/KeyInfoReferenceResolver.java
@@ -180,6 +180,7 @@ private KeyInfo resolveReferentKeyInfo(Element element, String baseURI, StorageR
         validateReference(referentElement);
 
         KeyInfo referent = new KeyInfo(referentElement, baseURI);
+        referent.setSecureValidation(secureValidation);
         referent.addStorageResolver(storage);
         return referent;
     }
@@ -198,7 +199,7 @@ private void validateReference(Element referentElement) throws XMLSecurityExcept
         }
 
         KeyInfo referent = new KeyInfo(referentElement, "");
-        if (referent.containsKeyInfoReference()) {
+        if (referent.containsKeyInfoReference() || referent.containsRetrievalMethod()) {
             if (secureValidation) {
                 throw new XMLSecurityException("KeyInfoReferenceResolver.InvalidReferentElement.ReferenceWithSecure");
             } else {
diff --git a/jdk/src/share/classes/com/sun/org/apache/xml/internal/security/resource/xmlsecurity_en.properties b/jdk/src/share/classes/com/sun/org/apache/xml/internal/security/resource/xmlsecurity_en.properties
index 4ae18fec4ce..59e5234c808 100644
--- a/jdk/src/share/classes/com/sun/org/apache/xml/internal/security/resource/xmlsecurity_en.properties
+++ b/jdk/src/share/classes/com/sun/org/apache/xml/internal/security/resource/xmlsecurity_en.properties
@@ -124,6 +124,7 @@ signature.Transform.ForbiddenTransform = Transform {0} is forbidden when secure
 signature.Transform.NotYetImplemented = Transform {0} not yet implemented
 signature.Transform.NullPointerTransform = Null pointer as URI. Programming bug?
 signature.Transform.UnknownTransform = Unknown transformation. No handler installed for URI {0}
+signature.Transform.XPathError = Error evaluating XPath expression
 signature.Transform.node = Current Node: {0}
 signature.Transform.nodeAndType = Current Node: {0}, type: {1} 
 signature.Util.BignumNonPositive = bigInteger.signum() must be positive
diff --git a/jdk/src/share/classes/com/sun/org/apache/xml/internal/security/signature/XMLSignatureInput.java b/jdk/src/share/classes/com/sun/org/apache/xml/internal/security/signature/XMLSignatureInput.java
index 82d34f0a67e..7199313c689 100644
--- a/jdk/src/share/classes/com/sun/org/apache/xml/internal/security/signature/XMLSignatureInput.java
+++ b/jdk/src/share/classes/com/sun/org/apache/xml/internal/security/signature/XMLSignatureInput.java
@@ -551,7 +551,7 @@ public void addNodeFilter(NodeFilter filter) {
                 convertToNodes();
             } catch (Exception e) {
                 throw new XMLSecurityRuntimeException(
-                    "signature.XMLSignatureInput.nodesetReference", e
+                    "signature.XMLSignatureInput.nodesetReference"
                 );
             }
         }
diff --git a/jdk/src/share/classes/com/sun/org/apache/xml/internal/security/transforms/implementations/TransformXPath.java b/jdk/src/share/classes/com/sun/org/apache/xml/internal/security/transforms/implementations/TransformXPath.java
index 749f1d0558d..7128b0191a5 100644
--- a/jdk/src/share/classes/com/sun/org/apache/xml/internal/security/transforms/implementations/TransformXPath.java
+++ b/jdk/src/share/classes/com/sun/org/apache/xml/internal/security/transforms/implementations/TransformXPath.java
@@ -147,11 +147,7 @@ public int isNodeInclude(Node currentNode) {
                 }
                 return 0;
             } catch (TransformerException e) {
-                Object[] eArgs = {currentNode};
-                throw new XMLSecurityRuntimeException("signature.Transform.node", eArgs, e);
-            } catch (Exception e) {
-                Object[] eArgs = {currentNode, currentNode.getNodeType()};
-                throw new XMLSecurityRuntimeException("signature.Transform.nodeAndType",eArgs, e);
+                throw new XMLSecurityRuntimeException("signature.Transform.XPathError");
             }
         }
 
diff --git a/jdk/src/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverLocalFilesystem.java b/jdk/src/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverLocalFilesystem.java
index 66ebd79de84..40f8a2cb4c6 100644
--- a/jdk/src/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverLocalFilesystem.java
+++ b/jdk/src/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverLocalFilesystem.java
@@ -38,8 +38,6 @@
  */
 public class ResolverLocalFilesystem extends ResourceResolverSpi {
 
-    private static final int FILE_URI_LENGTH = "file:/".length();
-
     private static final com.sun.org.slf4j.internal.Logger LOG =
         com.sun.org.slf4j.internal.LoggerFactory.getLogger(ResolverLocalFilesystem.class);
 
@@ -58,9 +56,7 @@ public XMLSignatureInput engineResolveURI(ResourceResolverContext context)
             // calculate new URI
             URI uriNew = getNewURI(context.uriToResolve, context.baseUri);
 
-            String fileName =
-                ResolverLocalFilesystem.translateUriToFilename(uriNew.toString());
-            InputStream inputStream = Files.newInputStream(Paths.get(fileName));
+            InputStream inputStream = Files.newInputStream(Paths.get(uriNew));
             XMLSignatureInput result = new XMLSignatureInput(inputStream);
             result.setSecureValidation(context.secureValidation);
 
@@ -72,41 +68,6 @@ public XMLSignatureInput engineResolveURI(ResourceResolverContext context)
         }
     }
 
-    /**
-     * Method translateUriToFilename
-     *
-     * @param uri
-     * @return the string of the filename
-     */
-    private static String translateUriToFilename(String uri) {
-
-        String subStr = uri.substring(FILE_URI_LENGTH);
-
-        if (subStr.indexOf("%20") > -1) {
-            int offset = 0;
-            int index = 0;
-            StringBuilder temp = new StringBuilder(subStr.length());
-            do {
-                index = subStr.indexOf("%20",offset);
-                if (index == -1) {
-                    temp.append(subStr.substring(offset));
-                } else {
-                    temp.append(subStr.substring(offset, index));
-                    temp.append(' ');
-                    offset = index + 3;
-                }
-            } while(index != -1);
-            subStr = temp.toString();
-        }
-
-        if (subStr.charAt(1) == ':') {
-            // we're running M$ Windows, so this works fine
-            return subStr;
-        }
-        // we're running some UNIX, so we have to prepend a slash
-        return "/" + subStr;
-    }
-
     /**
      * {@inheritDoc}
      */
diff --git a/jdk/src/share/classes/java/io/File.java b/jdk/src/share/classes/java/io/File.java
index e2321faab20..24fb1c92c24 100644
--- a/jdk/src/share/classes/java/io/File.java
+++ b/jdk/src/share/classes/java/io/File.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1994, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2022, 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
@@ -176,8 +176,9 @@ private static enum PathStatus { INVALID, CHECKED };
 
     /**
      * Check if the file has an invalid path. Currently, the inspection of
-     * a file path is very limited, and it only covers Nul character check.
-     * Returning true means the path is definitely invalid/garbage. But
+     * a file path is very limited, and it only covers Nul character check
+     * unless further checking is explicitly enabled by a system property.
+     * Returning true means the path is definitely invalid/garbage, but
      * returning false does not guarantee that the path is valid.
      *
      * @return true if the file path is invalid.
@@ -185,8 +186,7 @@ private static enum PathStatus { INVALID, CHECKED };
     final boolean isInvalid() {
         PathStatus s = status;
         if (s == null) {
-            s = (this.path.indexOf('\u0000') < 0) ? PathStatus.CHECKED
-                                                  : PathStatus.INVALID;
+            s = fs.isInvalid(this) ? PathStatus.INVALID : PathStatus.CHECKED;
             status = s;
         }
         return s == PathStatus.INVALID;
diff --git a/jdk/src/share/classes/java/io/FileSystem.java b/jdk/src/share/classes/java/io/FileSystem.java
index 9d73e79ed40..a30bb29d104 100644
--- a/jdk/src/share/classes/java/io/FileSystem.java
+++ b/jdk/src/share/classes/java/io/FileSystem.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2022, 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
@@ -87,6 +87,11 @@ abstract class FileSystem {
      */
     public abstract boolean isAbsolute(File f);
 
+    /**
+     * Tell whether the given abstract pathname is invalid.
+     */
+    public abstract boolean isInvalid(File f);
+
     /**
      * Resolve the given abstract pathname into absolute form.  Invoked by the
      * getAbsolutePath and getCanonicalPath methods in the File class.
diff --git a/jdk/src/share/classes/javax/crypto/EncryptedPrivateKeyInfo.java b/jdk/src/share/classes/javax/crypto/EncryptedPrivateKeyInfo.java
index 2d795bcaa8f..fdacf8a1b08 100644
--- a/jdk/src/share/classes/javax/crypto/EncryptedPrivateKeyInfo.java
+++ b/jdk/src/share/classes/javax/crypto/EncryptedPrivateKeyInfo.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2022, 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
@@ -80,12 +80,12 @@ public EncryptedPrivateKeyInfo(byte[] encoded) throws IOException {
                 "must be non-null");
         }
 
-        DerValue val = new DerValue(encoded);
+        this.encoded = encoded.clone();
+        DerValue val = DerValue.wrap(this.encoded);
         if (val.tag != DerValue.tag_Sequence) {
             throw new IOException("DER header error: no SEQ tag");
         }
 
-        this.encoded = encoded.clone();
         DerValue[] seq = new DerValue[2];
 
         seq[0] = val.data.getDerValue();
diff --git a/jdk/src/share/classes/jdk/jfr/consumer/ParserFactory.java b/jdk/src/share/classes/jdk/jfr/consumer/ParserFactory.java
index 4064e889d7f..d4e361065bc 100644
--- a/jdk/src/share/classes/jdk/jfr/consumer/ParserFactory.java
+++ b/jdk/src/share/classes/jdk/jfr/consumer/ParserFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2021, 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
@@ -263,6 +263,7 @@ public ArrayParser(Parser elementParser) {
         @Override
         public Object parse(RecordingInput input) throws IOException {
             final int size = input.readInt();
+            input.require(size, "Array size %d exceeds available data" );
             final Object[] array = new Object[size];
             for (int i = 0; i < size; i++) {
                 array[i] = elementParser.parse(input);
diff --git a/jdk/src/share/classes/jdk/jfr/internal/MetadataReader.java b/jdk/src/share/classes/jdk/jfr/internal/MetadataReader.java
index 481e3700dc6..6847189467e 100644
--- a/jdk/src/share/classes/jdk/jfr/internal/MetadataReader.java
+++ b/jdk/src/share/classes/jdk/jfr/internal/MetadataReader.java
@@ -49,6 +49,7 @@
 import jdk.jfr.SettingDescriptor;
 import jdk.jfr.ValueDescriptor;
 import jdk.jfr.internal.MetadataDescriptor.Element;
+import jdk.jfr.internal.consumer.RecordingInput;
 
 /**
  * Parses metadata.
@@ -64,6 +65,7 @@ final class MetadataReader {
     public MetadataReader(DataInput input) throws IOException {
         this.input = input;
         int size = input.readInt();
+        ((RecordingInput)input).require(size, "Metadata string pool size %d exceeds available data" );
         this.pool = new ArrayList<>(size);
         for (int i = 0; i < size; i++) {
             this.pool.add(input.readUTF());
diff --git a/jdk/src/share/classes/jdk/jfr/internal/consumer/RecordingInput.java b/jdk/src/share/classes/jdk/jfr/internal/consumer/RecordingInput.java
index b766894f3f0..01adcfeb542 100644
--- a/jdk/src/share/classes/jdk/jfr/internal/consumer/RecordingInput.java
+++ b/jdk/src/share/classes/jdk/jfr/internal/consumer/RecordingInput.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2021, 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
@@ -256,6 +256,7 @@ public String readEncodedString(byte encoding) throws IOException {
             return "";
         }
         int size = readInt();
+        require(size, "String size %d exceeds available data");
         if (encoding == STRING_ENCODING_CHAR_ARRAY) {
             char[] c = new char[size];
             for (int i = 0; i < size; i++) {
@@ -336,4 +337,13 @@ public long readLong() throws IOException {
         int b8 = readByte(); // read last byte raw
         return ret + (((long) (b8 & 0XFF)) << 56);
     }
+
+    // Purpose of this method is to prevent OOM by sanity check
+    // the minimum required number of bytes against what is available in
+    // segment/chunk/file
+    public void require(int minimumBytes, String errorMessage) throws IOException {
+        if (position + minimumBytes > size) {
+            throw new IOException(String.format(errorMessage, minimumBytes));
+        }
+    }
 }
diff --git a/jdk/src/share/classes/jdk/jfr/internal/tool/JSONWriter.java b/jdk/src/share/classes/jdk/jfr/internal/tool/JSONWriter.java
index 0e008c87325..5ca769792b9 100644
--- a/jdk/src/share/classes/jdk/jfr/internal/tool/JSONWriter.java
+++ b/jdk/src/share/classes/jdk/jfr/internal/tool/JSONWriter.java
@@ -187,7 +187,7 @@ private void printNull() {
     private void printDataStructureName(String text) {
         printIndent();
         print("\"");
-        print(text);
+        printEscaped(text);
         print("\": ");
     }
 
diff --git a/jdk/src/share/classes/jdk/jfr/internal/tool/XMLWriter.java b/jdk/src/share/classes/jdk/jfr/internal/tool/XMLWriter.java
index 2db1688239b..056642eb4aa 100644
--- a/jdk/src/share/classes/jdk/jfr/internal/tool/XMLWriter.java
+++ b/jdk/src/share/classes/jdk/jfr/internal/tool/XMLWriter.java
@@ -83,7 +83,11 @@ private void printEvent(RecordedEvent event) {
     }
 
     private void printAttribute(String name, String value) {
-        print(" ", name, "=\"", value, "\"");
+        print(" ");
+        print(name); // Only known strings
+        print("=\"");
+        printEscaped(value);
+        print("\"");
     }
 
     public void printObject(RecordedObject struct) {
diff --git a/jdk/src/share/classes/org/jcp/xml/dsig/internal/dom/DOMURIDereferencer.java b/jdk/src/share/classes/org/jcp/xml/dsig/internal/dom/DOMURIDereferencer.java
index 412e699386a..be5dfe1aa8d 100644
--- a/jdk/src/share/classes/org/jcp/xml/dsig/internal/dom/DOMURIDereferencer.java
+++ b/jdk/src/share/classes/org/jcp/xml/dsig/internal/dom/DOMURIDereferencer.java
@@ -141,8 +141,8 @@ public Data dereference(URIReference uriRef, XMLCryptoContext context)
 
         try {
             ResourceResolver apacheResolver =
-                ResourceResolver.getInstance(uriAttr, baseURI, false);
-            XMLSignatureInput in = apacheResolver.resolve(uriAttr, baseURI, false);
+                ResourceResolver.getInstance(uriAttr, baseURI, secVal);
+            XMLSignatureInput in = apacheResolver.resolve(uriAttr, baseURI, secVal);
             if (in.isOctetStream()) {
                 return new ApacheOctetStreamData(in);
             } else {
diff --git a/jdk/src/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java b/jdk/src/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java
index 83f915bd8d5..bbfc0602a1b 100644
--- a/jdk/src/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java
+++ b/jdk/src/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java
@@ -458,8 +458,8 @@ private void readObject(java.io.ObjectInputStream s)
                 if (!(memberType.isInstance(value) ||
                       value instanceof ExceptionProxy)) {
                     value = new AnnotationTypeMismatchExceptionProxy(
-                            value.getClass() + "[" + value + "]").setMember(
-                                annotationType.members().get(name));
+                                objectToString(value))
+                        .setMember(annotationType.members().get(name));
                 }
             }
             mv.put(name, value);
@@ -469,6 +469,15 @@ private void readObject(java.io.ObjectInputStream s)
         UnsafeAccessor.setMemberValues(this, mv);
     }
 
+    /*
+     * Create a textual representation of the argument without calling
+     * any overridable methods of the argument.
+     */
+    private static String objectToString(Object value) {
+        return value.getClass().getName() + "@" +
+            Integer.toHexString(System.identityHashCode(value));
+    }
+
     private static class UnsafeAccessor {
         private static final sun.misc.Unsafe unsafe;
         private static final long typeOffset;
diff --git a/jdk/src/share/classes/sun/security/tools/keytool/Main.java b/jdk/src/share/classes/sun/security/tools/keytool/Main.java
index 11ed55d9d40..00287e7b4f3 100644
--- a/jdk/src/share/classes/sun/security/tools/keytool/Main.java
+++ b/jdk/src/share/classes/sun/security/tools/keytool/Main.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2022, 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
@@ -1983,6 +1983,9 @@ private void doPrintEntry(String label, String alias, PrintStream out)
                 out.println(mf);
                 dumpCert(cert, out);
             } else if (debug) {
+                for (Entry.Attribute attr : keyStore.getEntry(alias, null).getAttributes()) {
+                    System.out.println("Attribute " + attr.getName() + ": " + attr.getValue());
+                }
                 out.println(cert.toString());
             } else {
                 out.println("trustedCertEntry, ");
diff --git a/jdk/src/share/classes/sun/security/util/DerIndefLenConverter.java b/jdk/src/share/classes/sun/security/util/DerIndefLenConverter.java
index be4314db7ca..0f35f876745 100644
--- a/jdk/src/share/classes/sun/security/util/DerIndefLenConverter.java
+++ b/jdk/src/share/classes/sun/security/util/DerIndefLenConverter.java
@@ -146,15 +146,14 @@ private void parseTag() throws IOException {
      * then skip the tag and its 1 byte length of zero.
      */
     private void writeTag() {
-        if (dataPos == dataSize) {
-            return;
-        }
-        assert dataPos + 1 < dataSize;
-        if (isEOC(data, dataPos)) {
-            dataPos += 2;  // skip tag and length
-            writeTag();
-        } else {
-            newData[newDataPos++] = data[dataPos++];
+        while (dataPos < dataSize) {
+            assert dataPos + 1 < dataSize;
+            if (isEOC(data, dataPos)) {
+                dataPos += 2;  // skip tag and length
+            } else {
+                newData[newDataPos++] = data[dataPos++];
+                break;
+            }
         }
     }
 
diff --git a/jdk/src/share/classes/sun/security/util/DerValue.java b/jdk/src/share/classes/sun/security/util/DerValue.java
index 54639a0d083..b7d1714769e 100644
--- a/jdk/src/share/classes/sun/security/util/DerValue.java
+++ b/jdk/src/share/classes/sun/security/util/DerValue.java
@@ -306,6 +306,34 @@ public DerValue(byte[] buf) throws IOException {
         data = init(true, new ByteArrayInputStream(buf, offset, len), allowBER);
     }
 
+    /**
+     * Wraps a byte array as a single DerValue.
+     *
+     * Attention: no cloning is made.
+     *
+     * @param buf the byte array containing the DER-encoded datum
+     * @returns a new DerValue
+     */
+    public static DerValue wrap(byte[] buf)
+            throws IOException {
+        return wrap(buf, 0, buf.length);
+    }
+
+    /**
+     * Wraps a byte array as a single DerValue.
+     *
+     * Attention: no cloning is made.
+     *
+     * @param buf the byte array containing the DER-encoded datum
+     * @param offset where the encoded datum starts inside {@code buf}
+     * @param len length of bytes to parse inside {@code buf}
+     * @returns a new DerValue
+     */
+    public static DerValue wrap(byte[] buf, int offset, int len)
+            throws IOException {
+        return new DerValue(buf, offset, len);
+    }
+
     /**
      * Get an ASN.1/DER encoded datum from part of a buffer.
      * That part of the buffer must hold exactly one datum, including
diff --git a/jdk/src/share/classes/sun/security/util/ObjectIdentifier.java b/jdk/src/share/classes/sun/security/util/ObjectIdentifier.java
index 783e4b3cdf9..1f02ada843d 100644
--- a/jdk/src/share/classes/sun/security/util/ObjectIdentifier.java
+++ b/jdk/src/share/classes/sun/security/util/ObjectIdentifier.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2021, 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
@@ -443,7 +443,7 @@ private int[] toIntArray() {
             if ((encoding[i] & 0x80) == 0) {
                 // one section [fromPos..i]
                 if (i - fromPos + 1 > 4) {
-                    BigInteger big = new BigInteger(pack(encoding, fromPos, i-fromPos+1, 7, 8));
+                    BigInteger big = new BigInteger(1, pack(encoding, fromPos, i-fromPos+1, 7, 8));
                     if (fromPos == 0) {
                         result[which++] = 2;
                         BigInteger second = big.subtract(BigInteger.valueOf(80));
@@ -508,7 +508,7 @@ public String toString() {
                         sb.append('.');
                     }
                     if (i - fromPos + 1 > 4) { // maybe big integer
-                        BigInteger big = new BigInteger(pack(encoding, fromPos, i-fromPos+1, 7, 8));
+                        BigInteger big = new BigInteger(1, pack(encoding, fromPos, i-fromPos+1, 7, 8));
                         if (fromPos == 0) {
                             // first section encoded with more than 4 bytes,
                             // must be 2.something
@@ -741,6 +741,11 @@ private static void checkOtherComponent(int i, BigInteger num) throws IOExceptio
     }
 
     private static void checkOidSize(int oidLength) throws IOException {
+        if (oidLength < 0) {
+            throw new IOException("ObjectIdentifier encoded length was " +
+                    "negative: " + oidLength);
+        }
+
         if (oidLength > MAXIMUM_OID_SIZE) {
             throw new IOException(
                     "ObjectIdentifier encoded length exceeds " +
diff --git a/jdk/src/solaris/classes/java/io/UnixFileSystem.java b/jdk/src/solaris/classes/java/io/UnixFileSystem.java
index 3cb77a95459..63a516e68a1 100644
--- a/jdk/src/solaris/classes/java/io/UnixFileSystem.java
+++ b/jdk/src/solaris/classes/java/io/UnixFileSystem.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2022, 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
@@ -131,6 +131,11 @@ public boolean isAbsolute(File f) {
         return (f.getPrefixLength() != 0);
     }
 
+    @Override
+    public boolean isInvalid(File f) {
+        return f.getPath().indexOf('\u0000') < 0 ? false : true;
+    }
+
     public String resolve(File f) {
         if (isAbsolute(f)) return f.getPath();
         SecurityManager sm = System.getSecurityManager();
diff --git a/jdk/src/windows/classes/java/io/WinNTFileSystem.java b/jdk/src/windows/classes/java/io/WinNTFileSystem.java
index 9b22dba018c..450f17f1531 100644
--- a/jdk/src/windows/classes/java/io/WinNTFileSystem.java
+++ b/jdk/src/windows/classes/java/io/WinNTFileSystem.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2022, 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
@@ -25,8 +25,13 @@
 
 package java.io;
 
+import java.net.URI;
+import java.nio.file.InvalidPathException;
+import java.nio.file.Path;
 import java.security.AccessController;
 import java.util.Locale;
+
+import sun.nio.fs.DefaultFileSystemProvider;
 import sun.security.action.GetPropertyAction;
 
 /**
@@ -37,11 +42,34 @@
  */
 class WinNTFileSystem extends FileSystem {
 
+    /**
+     * Always use the internal default file system, in case it was modified
+     * with java.nio.file.spi.DefaultFileSystemProvider.
+     */
+    private static final java.nio.file.FileSystem builtInFS =
+            DefaultFileSystemProvider.create()
+                    .getFileSystem(URI.create("file:///"));
+
     private final char slash;
     private final char altSlash;
     private final char semicolon;
     private final String userDir;
 
+    // Whether to enable alternative data streams (ADS) by suppressing
+    // checking the path for invalid characters, in particular ":".
+    // ADS support will be enabled if and only if the property is set and
+    // is the empty string or is equal, ignoring case, to the string "true".
+    // By default ADS support is disabled.
+    private static final boolean ENABLE_ADS;
+    static {
+        String enableADS = GetPropertyAction.privilegedGetProperty("jdk.io.File.enableADS");
+        if (enableADS != null) {
+            ENABLE_ADS = "".equals(enableADS) || Boolean.parseBoolean(enableADS);
+        } else {
+            ENABLE_ADS = false;
+        }
+    }
+
     public WinNTFileSystem() {
         slash = AccessController.doPrivileged(
             new GetPropertyAction("file.separator")).charAt(0);
@@ -300,6 +328,36 @@ public boolean isAbsolute(File f) {
                 || (pl == 3));
     }
 
+    @Override
+    public boolean isInvalid(File f) {
+        if (f.getPath().indexOf('\u0000') >= 0)
+            return true;
+
+        if (ENABLE_ADS)
+            return false;
+
+        // Invalid if there is a ":" at a position greater than 1, or if there
+        // is a ":" at position 1 and the first character is not a letter
+        String pathname = f.getPath();
+        int lastColon = pathname.lastIndexOf(":");
+
+        // Valid if there is no ":" present or if the last ":" present is
+        // at index 1 and the first character is a latter
+        if (lastColon < 0 ||
+            (lastColon == 1 && isLetter(pathname.charAt(0))))
+            return false;
+
+        // Invalid if path creation fails
+        Path path = null;
+        try {
+            path = builtInFS.getPath(pathname);
+            return false;
+        } catch (InvalidPathException ignored) {
+        }
+
+        return true;
+    }
+
     @Override
     public String resolve(File f) {
         String path = f.getPath();
diff --git a/jdk/test/javax/xml/jaxp/XPath/8284548/InvalidXPath.java b/jdk/test/javax/xml/jaxp/XPath/8284548/InvalidXPath.java
new file mode 100644
index 00000000000..478f4212d5b
--- /dev/null
+++ b/jdk/test/javax/xml/jaxp/XPath/8284548/InvalidXPath.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2022, SAP SE. 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.
+ */
+
+/*
+ * @test
+ * @bug 8284548
+ * @summary Test whether the expected exception is thrown when
+ *           trying to compile an invalid XPath expression.
+ * @run main InvalidXPath
+ */
+
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+public class InvalidXPath {
+
+    public static void main(String... args) {
+        // define an invalid XPath expression
+        final String invalidXPath = ">>";
+
+        // expect XPathExpressionException when the invalid XPath expression is compiled
+        try {
+            XPathFactory.newInstance().newXPath().compile(invalidXPath);
+        } catch (XPathExpressionException e) {
+            System.out.println("Caught expected exception: " + e.getClass().getName() +
+                    "(" + e.getMessage() + ").");
+        } catch (Exception e) {
+            System.out.println("Caught unexpected exception: " + e.getClass().getName() +
+                    "(" + e.getMessage() + ")!");
+            throw e;
+        }
+    }
+}
diff --git a/jdk/test/lib/testlibrary/jdk/testlibrary/SecurityTools.java b/jdk/test/lib/testlibrary/jdk/testlibrary/SecurityTools.java
index 71a4b852deb..e0ec99a2181 100644
--- a/jdk/test/lib/testlibrary/jdk/testlibrary/SecurityTools.java
+++ b/jdk/test/lib/testlibrary/jdk/testlibrary/SecurityTools.java
@@ -23,12 +23,15 @@
 
 package jdk.testlibrary;
 
+import java.io.Closeable;
 import java.io.File;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
@@ -124,4 +127,66 @@ public static OutputAnalyzer jarsigner(String args) throws Exception {
     public static OutputAnalyzer jarsigner(String... args) throws Exception {
         return jarsigner(Arrays.asList(args));
     }
+
+    // Create a temporary keychain in macOS and use it. The original
+    // keychains will be restored when the object is closed.
+    public static class TemporaryKeychain implements Closeable {
+        // name of new keychain
+        private final String newChain;
+        // names of the original keychains
+        private final List oldChains;
+
+        public TemporaryKeychain(String name) {
+            File f = new File(name + ".keychain-db");
+            newChain = f.getAbsolutePath();
+            try {
+                oldChains = new ArrayList();
+                List ls = ProcessTools.executeProcess("security", "list-keychains")
+                        .shouldHaveExitValue(0)
+                        .asLines();
+                for (String l: ls) {
+                    l = l.trim();
+                    if (l.startsWith("\"")) {
+                        oldChains.add(l.substring(1, l.length() - 1));
+                    } else {
+                        oldChains.add(l);
+                    }
+                }
+                if (!f.exists()) {
+                    ProcessTools.executeProcess("security", "create-keychain", "-p", "changeit", newChain)
+                            .shouldHaveExitValue(0);
+                }
+                ProcessTools.executeProcess("security", "unlock-keychain", "-p", "changeit", newChain)
+                        .shouldHaveExitValue(0);
+                ProcessTools.executeProcess("security", "list-keychains", "-s", newChain)
+                        .shouldHaveExitValue(0);
+            } catch (Throwable t) {
+                if (t instanceof RuntimeException) {
+                    throw (RuntimeException)t;
+                } else {
+                    throw new RuntimeException(t);
+                }
+            }
+        }
+
+        public String chain() {
+            return newChain;
+        }
+
+        @Override
+        public void close() throws IOException {
+            List cmds = new ArrayList(Arrays.asList("security", "list-keychains", "-s"));
+            cmds.addAll(Collections.unmodifiableList(oldChains));
+            try {
+                ProcessTools.executeProcess(cmds.toArray(new String[0]))
+                        .shouldHaveExitValue(0);
+            } catch (Throwable t) {
+                if (t instanceof RuntimeException) {
+                    throw (RuntimeException)t;
+                } else {
+                    throw new RuntimeException(t);
+                }
+            }
+        }
+    }
 }
diff --git a/jdk/test/sun/security/lib/cacerts/VerifyCACerts.java b/jdk/test/sun/security/lib/cacerts/VerifyCACerts.java
index 74972697bb7..12ad3254b45 100644
--- a/jdk/test/sun/security/lib/cacerts/VerifyCACerts.java
+++ b/jdk/test/sun/security/lib/cacerts/VerifyCACerts.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2022, 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
@@ -261,6 +261,8 @@ public class VerifyCACerts {
             add("luxtrustglobalrootca [jdk]");
             // Valid until: Wed Mar 17 11:33:33 PDT 2021
             add("quovadisrootca [jdk]");
+            // Valid until: Sat May 21 04:00:00 GMT 2022
+            add("geotrustglobalca [jdk]");
         }
     };