8206132: DOM parser does not honor DocumentBuilderFactory.setExpandEntityReferences(false)

Reviewed-by: rriggs, lancea
This commit is contained in:
Joe Wang 2019-02-05 09:57:35 -08:00
parent 4aeeb2f045
commit b63c4ce810
5 changed files with 344 additions and 109 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
*/
/*
@ -73,7 +73,7 @@ import jdk.xml.internal.SecuritySupport;
* @author Eric Ye, IBM
* @author Sunitha Reddy, SUN Microsystems
*
* @LastModified: Sep 2017
* @LastModified: Jan 2019
*/
public class XMLDocumentFragmentScannerImpl
extends XMLScanner
@ -163,6 +163,10 @@ public class XMLDocumentFragmentScannerImpl
protected static final String STANDARD_URI_CONFORMANT =
Constants.XERCES_FEATURE_PREFIX +Constants.STANDARD_URI_CONFORMANT_FEATURE;
/** Feature id: create entity ref nodes. */
protected static final String CREATE_ENTITY_REF_NODES =
Constants.XERCES_FEATURE_PREFIX + Constants.CREATE_ENTITY_REF_NODES_FEATURE;
/** Property identifier: Security property manager. */
private static final String XML_SECURITY_PROPERTY_MANAGER =
Constants.XML_SECURITY_PROPERTY_MANAGER;
@ -322,6 +326,9 @@ public class XMLDocumentFragmentScannerImpl
/** Xerces Feature: Disallow doctype declaration. */
protected boolean fDisallowDoctype = false;
/** Create entity reference nodes. */
protected boolean fCreateEntityRefNodes = false;
/**
* CDATA chunk size limit
*/
@ -596,6 +603,8 @@ public class XMLDocumentFragmentScannerImpl
fSecurityManager = (XMLSecurityManager)componentManager.getProperty(Constants.SECURITY_MANAGER, null);
fNotifyBuiltInRefs = componentManager.getFeature(NOTIFY_BUILTIN_REFS, false);
fCreateEntityRefNodes = componentManager.getFeature(CREATE_ENTITY_REF_NODES, fCreateEntityRefNodes);
Object resolver = componentManager.getProperty(ENTITY_RESOLVER, null);
fExternalSubsetResolver = (resolver instanceof ExternalSubsetResolver) ?
(ExternalSubsetResolver) resolver : null;
@ -1837,14 +1846,20 @@ public class XMLDocumentFragmentScannerImpl
} else
reportFatalError("EntityNotDeclared", new Object[]{name});
}
//we are starting the entity even if the entity was not declared
//if that was the case it its taken care in XMLEntityManager.startEntity()
//we immediately call the endEntity. Application gets to know if there was
//any entity that was not declared.
fEntityManager.startEntity(true, name, false);
//set the scaner state to content.. parser will automatically revive itself at any point of time.
//setScannerState(SCANNER_STATE_CONTENT);
//return true ;
// create EntityReference only
if (fCreateEntityRefNodes) {
fDocumentHandler.startGeneralEntity(name, null, null, null);
} else {
//we are starting the entity even if the entity was not declared
//if that was the case it its taken care in XMLEntityManager.startEntity()
//we immediately call the endEntity. Application gets to know if there was
//any entity that was not declared.
fEntityManager.startEntity(true, name, false);
//set the scaner state to content.. parser will automatically revive itself at any point of time.
//setScannerState(SCANNER_STATE_CONTENT);
//return true ;
}
} // scanEntityReference()
// utility methods

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2017, 2019 Oracle and/or its affiliates. All rights reserved.
*/
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
@ -84,7 +84,7 @@ import org.xml.sax.SAXException;
* @author Andy Clark, IBM
* @author Elena Litani, IBM
*
* @LastModified: Nov 2017
* @LastModified: Jan 2019
*/
public class AbstractDOMParser extends AbstractXMLDocumentParser {
@ -491,8 +491,10 @@ public class AbstractDOMParser extends AbstractXMLDocumentParser {
if (DEBUG_EVENTS) {
System.out.println ("==>startGeneralEntity ("+name+")");
if (DEBUG_BASEURI) {
System.out.println (" expandedSystemId( **baseURI): "+identifier.getExpandedSystemId ());
System.out.println (" baseURI:"+ identifier.getBaseSystemId ());
System.out.println (" expandedSystemId( **baseURI): " +
identifier == null ? null : identifier.getExpandedSystemId());
System.out.println (" baseURI:" +
identifier == null ? null : identifier.getBaseSystemId());
}
}
@ -512,7 +514,7 @@ public class AbstractDOMParser extends AbstractXMLDocumentParser {
EntityReferenceImpl erImpl =(EntityReferenceImpl)er;
// set base uri
erImpl.setBaseURI (identifier.getExpandedSystemId ());
erImpl.setBaseURI (identifier == null ? null : identifier.getExpandedSystemId());
if (fDocumentType != null) {
// set actual encoding
NamedNodeMap entities = fDocumentType.getEntities ();
@ -528,12 +530,17 @@ public class AbstractDOMParser extends AbstractXMLDocumentParser {
}
fInEntityRef = true;
fCurrentNode.appendChild (er);
fCurrentNode = er;
if (!fCreateEntityRefNodes) {
fCurrentNode = er;
} else {
((NodeImpl)er).setReadOnly (true, true);
}
}
else {
int er =
fDeferredDocumentImpl.createDeferredEntityReference (name, identifier.getExpandedSystemId ());
int er = fDeferredDocumentImpl.createDeferredEntityReference (name,
identifier == null ? null : identifier.getExpandedSystemId ());
if (fDocumentTypeIndex != -1) {
// find corresponding Entity decl
int node = fDeferredDocumentImpl.getLastChild (fDocumentTypeIndex, false);
@ -552,7 +559,10 @@ public class AbstractDOMParser extends AbstractXMLDocumentParser {
}
}
fDeferredDocumentImpl.appendChild (fCurrentNodeIndex, er);
fCurrentNodeIndex = er;
if (!fCreateEntityRefNodes) {
fCurrentNodeIndex = er;
}
}
} // startGeneralEntity(String,XMLResourceIdentifier, Augmentations)

View File

@ -0,0 +1,188 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package dom;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.testng.Assert;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.EntityReference;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.SAXException;
/*
* @test
* @bug 8206132
* @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest
* @run testng dom.DOMFeatureTest
* @summary Tests DOM features.
*/
@Listeners({jaxp.library.BasePolicy.class})
public class DOMFeatureTest {
private static final String XML1 = "<?xml version=\"1.0\"?>\n"
+ "<!DOCTYPE document [\n"
+ " <!ENTITY author \"William Shakespeare\">\n"
+ " <!ELEMENT document (title)>\n"
+ " <!ELEMENT title (#PCDATA)>\n"
+ "]>\n"
+ "<document>\n"
+ " <title>&author;</title>\n"
+ "</document>";
private static final String XML2 = "<?xml version=\"1.0\"?>\n"
+ "<!DOCTYPE document [\n"
+ " <!ENTITY author \"William Shakespeare\">\n"
+ " <!ELEMENT document (title)>\n"
+ " <!ELEMENT title (#PCDATA|chapter)*>\n"
+ " <!ELEMENT chapter (#PCDATA)>\n"
+ "]>\n"
+ "<document>\n"
+ " <title>&author; Hamlet<chapter>Chapter 1</chapter></title>\n"
+ "</document>";
private static final String XML3 = "<?xml version=\"1.0\"?>\n"
+ "<!DOCTYPE document [\n"
+ " <!ENTITY author SYSTEM \"https://openjdk_java_net/author.dtd\">"
+ " <!ELEMENT document (title)>\n"
+ " <!ELEMENT title (#PCDATA|chapter)*>\n"
+ " <!ELEMENT chapter (#PCDATA)>\n"
+ "]>\n"
+ "<document>\n"
+ " <title>&author; Hamlet<chapter>Chapter 1</chapter></title>\n"
+ "</document>";
/*
* DataProvider: for testing the EntityExpansion feature
* Data columns: case number, feature setting (true/false), xml file,
* number of nodes expected, text content expected, element if any
*/
@DataProvider(name = "EntityExpansion")
Object[][] getData() throws Exception {
return new Object[][]{
{1, true, XML1, 1, "William Shakespeare", null},
{2, true, XML2, 2, "William Shakespeare Hamlet", "chapter"},
{3, false, XML1, 1, null, null},
{4, false, XML2, 3, " Hamlet", "chapter"},
{4, false, XML3, 3, " Hamlet", "chapter"},
};
}
/*
* DataProvider: for testing the EntityExpansion feature
* Data columns: feature setting (true/false), xml file
*/
@DataProvider(name = "EntityExpansion1")
Object[][] getData1() throws Exception {
return new Object[][]{
{true, XML3},
};
}
/**
* Verifies the EntityExpansion feature.
* @param caseNo the case number
* @param feature flag indicating the setting of the feature
* @param xml the XML string
* @param n the number of nodes expected
* @param expectedText expected Text string
* @param expectedElement expected Element
* @throws Exception
*/
@Test(dataProvider = "EntityExpansion")
public void testEntityExpansion(int caseNo, boolean feature, String xml,
int n, String expectedText, String expectedElement) throws Exception {
final Document doc = getDocument(feature, xml);
final Element e = (Element) doc.getElementsByTagName("title").item(0);
final NodeList nl = e.getChildNodes();
switch (caseNo) {
case 1:
// The DOM tree should contain just the Text node
Assert.assertTrue(nl.item(0) instanceof Text);
Assert.assertEquals(nl.item(0).getNodeValue(), expectedText);
Assert.assertEquals(nl.getLength(), n);
break;
case 2:
// The DOM tree contains the Text node and an Element (chapter)
Assert.assertTrue(nl.item(0) instanceof Text);
Assert.assertEquals(nl.item(0).getNodeValue(), expectedText);
Assert.assertTrue(nl.item(1) instanceof Element);
Assert.assertEquals(nl.item(1).getNodeName(), expectedElement);
Assert.assertEquals(nl.getLength(), n);
break;
case 3:
// The DOM tree contains just the EntityReference node
Assert.assertTrue(nl.item(0) instanceof EntityReference);
Assert.assertEquals(nl.item(0).getNodeValue(), null);
Assert.assertEquals(nl.getLength(), n);
break;
case 4:
// The DOM tree contains a EntityReference, Text and an Element
Assert.assertTrue(nl.item(0) instanceof EntityReference);
Assert.assertEquals(nl.item(0).getNodeValue(), null);
Assert.assertTrue(nl.item(1) instanceof Text);
Assert.assertEquals(nl.item(1).getNodeValue(), expectedText);
Assert.assertTrue(nl.item(2) instanceof Element);
Assert.assertEquals(nl.item(2).getNodeName(), expectedElement);
Assert.assertEquals(nl.getLength(), n);
break;
}
}
/**
* Verifies the EntityExpansion feature. When the feature is set to true, the
* parser will attempt to resolve the external reference, that in turn will
* result in an Exception.
* @param feature flag indicating the setting of the feature
* @param xml the XML string
* @throws Exception: when a non-existent external reference is encountered
*/
@Test(dataProvider = "EntityExpansion1", expectedExceptions = java.net.UnknownHostException.class)
public void testEntityExpansion1(boolean feature, String xml)
throws Exception {
final Document doc = getDocument(feature, xml);
final Element e = (Element) doc.getElementsByTagName("title").item(0);
final NodeList nl = e.getChildNodes();
}
private static Document getDocument(boolean expand, String xml)
throws SAXException, IOException, ParserConfigurationException {
final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setExpandEntityReferences(expand);
final DocumentBuilder docBuilder = dbf.newDocumentBuilder();
InputStream a = new ByteArrayInputStream(xml.getBytes());
Document out = docBuilder.parse(a);
return out;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2019, 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
@ -125,7 +125,6 @@ public class ElementTraversal {
Document doc = null;
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setExpandEntityReferences(false);
DocumentBuilder db = dbf.newDocumentBuilder();
doc = db.parse(xmlFile);
} catch (ParserConfigurationException | SAXException | IOException e) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2019, 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
@ -35,6 +35,7 @@ import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.testng.Assert;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import org.w3c.dom.DOMError;
@ -52,7 +53,7 @@ import org.xml.sax.SAXException;
/*
* @test
* @bug 8080906 8114834
* @bug 8080906 8114834 8206132
* @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest
* @run testng/othervm -DrunSecMngr=true dom.ls.LSSerializerTest
* @run testng/othervm dom.ls.LSSerializerTest
@ -225,31 +226,111 @@ public class LSSerializerTest {
Assert.assertEquals(XML11_DOCUMENT_OUTPUT, defaultSerialization, "Invalid serialization of XML 1.1 document: ");
}
// XML source
private static final String XML =
"<?xml version=\"1.1\" encoding=\"UTF-16\"?>\n" +
"<!DOCTYPE author [\n" +
" <!ENTITY name \"Jo Smith\">" +
" <!ENTITY name1 \"&name;\">" +
" <!ENTITY name2 \"&name1;\">" +
"<!ENTITY ele \"<aa><bb>text</bb></aa>\">" +
" <!ENTITY ele1 \"&ele;\">" +
" <!ENTITY ele2 \"&ele1;\">" +
" ]>" +
" <author><a>&name1;</a>" +
"<b>b &name2; &name1; b</b>" +
"<c> &name; </c>" +
"<d>&ele1;d</d>" +
"<e> &ele2;eee </e>" +
"<f>&lt;att&gt;</f>" +
"<g> &ele; g</g>" +
"<h>&ele2;</h></author>" ;
// result when "entities" = true, equvalent to setting ExpandEntityReference to false
private static final String RESULT_TRUE =
"<?xml version=\"1.1\" encoding=\"UTF-16\"?><!DOCTYPE author [ \n" +
"<!ENTITY name 'Jo Smith'>\n" +
"<!ENTITY name1 '&name;'>\n" +
"<!ENTITY name2 '&name1;'>\n" +
"<!ENTITY ele '<aa><bb>text</bb></aa>'>\n" +
"<!ENTITY ele1 '&ele;'>\n" +
"<!ENTITY ele2 '&ele1;'>\n" +
"]>\n" +
"<author>\n" +
" <a>&name1;</a>\n" +
" <b>b &name2;&name1; b</b>\n" +
" <c>&name;</c>\n" +
" <d>&ele1;d</d>\n" +
" <e>&ele2;eee </e>\n" +
" <f>&lt;att&gt;</f>\n" +
" <g>&ele; g</g>\n" +
" <h>&ele2;</h>\n" +
"</author>\n";
// result when "entities" = false, equvalent to setting ExpandEntityReference to true
private static final String RESULT_FALSE =
"<?xml version=\"1.1\" encoding=\"UTF-16\"?><!DOCTYPE author [ \n" +
"<!ENTITY name 'Jo Smith'>\n" +
"<!ENTITY name1 '&name;'>\n" +
"<!ENTITY name2 '&name1;'>\n" +
"<!ENTITY ele '<aa><bb>text</bb></aa>'>\n" +
"<!ENTITY ele1 '&ele;'>\n" +
"<!ENTITY ele2 '&ele1;'>\n" +
"]>\n" +
"<author>\n" +
" <a>Jo Smith</a>\n" +
" <b>b Jo Smith Jo Smith b</b>\n" +
" <c> Jo Smith </c>\n" +
" <d>\n" +
" <aa>\n" +
" <bb>text</bb>\n" +
" </aa>\n" +
" d\n" +
" </d>\n" +
" <e>\n" +
" <aa>\n" +
" <bb>text</bb>\n" +
" </aa>\n" +
" eee \n" +
" </e>\n" +
" <f>&lt;att&gt;</f>\n" +
" <g>\n" +
" <aa>\n" +
" <bb>text</bb>\n" +
" </aa>\n" +
" g\n" +
" </g>\n" +
" <h>\n" +
" <aa>\n" +
" <bb>text</bb>\n" +
" </aa>\n" +
" </h>\n" +
"</author>\n";
/*
* @bug 8114834 test entity reference, nested entity reference when entities
* is true and false
* DataProvider: for testing the entities parameter
* Data columns: xml source, entities setting, expected result
*/
@Test
public void testEntityReference() throws Exception {
final String XML_DOCUMENT = "<?xml version=\"1.1\" encoding=\"UTF-16\"?>\n" +
"<!DOCTYPE author [\n" +
" <!ENTITY name \"Jo Smith\">" +
" <!ENTITY name1 \"&name;\">" +
" <!ENTITY name2 \"&name1;\">" +
"<!ENTITY ele \"<aa><bb>text</bb></aa>\">" +
" <!ENTITY ele1 \"&ele;\">" +
" <!ENTITY ele2 \"&ele1;\">" +
" ]>" +
" <author><a>&name1;</a>" +
"<b>b &name2; &name1; b</b>" +
"<c> &name; </c>" +
"<d>&ele1;d</d>" +
"<e> &ele2;eee </e>" +
"<f>&lt;att&gt;</f>" +
"<g> &ele; g</g>" +
"<h>&ele2;</h></author>" ;
@DataProvider(name = "entities")
Object[][] getData() throws Exception {
return new Object[][]{
{XML, Boolean.TRUE, RESULT_TRUE},
{XML, Boolean.FALSE, RESULT_FALSE},
};
}
/**
* Tests serializing DOM Document with DOMConfiguration's "entities" parameter.
*
* @param source the XML source
* @param entities the entities parameter setting
* @param expected expected string result
* @throws Exception
* @bug 8114834 8206132
*/
@Test(dataProvider = "entities")
public void testEntityReference(String source, Boolean entities, String expected)
throws Exception {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
@ -257,76 +338,18 @@ public class LSSerializerTest {
DOMImplementationLS domImplementationLS = (DOMImplementationLS) domImplementation;
LSParser domParser = domImplementationLS.createLSParser(MODE_SYNCHRONOUS, null);
domParser.getDomConfig().setParameter("entities", Boolean.TRUE);
domParser.getDomConfig().setParameter("entities", entities);
LSInput src = domImplementationLS.createLSInput();
src.setStringData(XML_DOCUMENT);
src.setStringData(source);
Document document = domParser.parse(src);
LSSerializer lsSerializer = domImplementationLS.createLSSerializer();
lsSerializer.getDomConfig().setParameter("format-pretty-print", true);
System.out.println("test with default entities is " + lsSerializer.getDomConfig().getParameter("entities"));
Assert.assertEquals(lsSerializer.writeToString(document),
"<?xml version=\"1.1\" encoding=\"UTF-16\"?><!DOCTYPE author [ \n" +
"<!ENTITY name 'Jo Smith'>\n" +
"<!ENTITY name1 '&name;'>\n" +
"<!ENTITY name2 '&name1;'>\n" +
"<!ENTITY ele '<aa><bb>text</bb></aa>'>\n" +
"<!ENTITY ele1 '&ele;'>\n" +
"<!ENTITY ele2 '&ele1;'>\n" +
"]>\n" +
"<author>\n" +
" <a>&name1;Jo Smith</a>\n" +
" <b>b &name2;Jo Smith &name1;Jo Smith b</b>\n" +
" <c>&name;Jo Smith </c>\n" +
" <d>&ele1;d</d>\n" +
" <e>&ele2;eee </e>\n" +
" <f>&lt;att&gt;</f>\n" +
" <g>&ele; g</g>\n" +
" <h>&ele2;</h>\n" +
"</author>\n");
lsSerializer.getDomConfig().setParameter("entities", Boolean.FALSE);
System.out.println("test with entities is false");
Assert.assertEquals(lsSerializer.writeToString(document),
"<?xml version=\"1.1\" encoding=\"UTF-16\"?><!DOCTYPE author [ \n" +
"<!ENTITY name 'Jo Smith'>\n" +
"<!ENTITY name1 '&name;'>\n" +
"<!ENTITY name2 '&name1;'>\n" +
"<!ENTITY ele '<aa><bb>text</bb></aa>'>\n" +
"<!ENTITY ele1 '&ele;'>\n" +
"<!ENTITY ele2 '&ele1;'>\n" +
"]>\n" +
"<author>\n" +
" <a>&name;Jo Smith</a>\n" +
" <b>b &name;Jo Smith &name;Jo Smith b</b>\n" +
" <c>&name;Jo Smith </c>\n" +
" <d>\n" +
" <aa>\n" +
" <bb>text</bb>\n" +
" </aa>\n" +
" d\n" +
" </d>\n" +
" <e>\n" +
" <aa>\n" +
" <bb>text</bb>\n" +
" </aa>\n" +
" eee \n" +
" </e>\n" +
" <f>&lt;att&gt;</f>\n" +
" <g>\n" +
" <aa>\n" +
" <bb>text</bb>\n" +
" </aa>\n" +
" g\n" +
" </g>\n" +
" <h>\n" +
" <aa>\n" +
" <bb>text</bb>\n" +
" </aa>\n" +
" </h>\n" +
"</author>\n");
System.out.println("test with default entities is " +
lsSerializer.getDomConfig().getParameter("entities"));
String result = lsSerializer.writeToString(document);
Assert.assertEquals(result, expected);
}
}