diff --git a/spring-oxm/src/main/java/org/springframework/oxm/jaxb/Jaxb2Marshaller.java b/spring-oxm/src/main/java/org/springframework/oxm/jaxb/Jaxb2Marshaller.java index 14a784551a..fd0d72bf67 100644 --- a/spring-oxm/src/main/java/org/springframework/oxm/jaxb/Jaxb2Marshaller.java +++ b/spring-oxm/src/main/java/org/springframework/oxm/jaxb/Jaxb2Marshaller.java @@ -21,6 +21,7 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.io.StringReader; import java.io.UnsupportedEncodingException; import java.lang.reflect.GenericArrayType; import java.lang.reflect.ParameterizedType; @@ -70,6 +71,7 @@ import javax.xml.validation.SchemaFactory; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.w3c.dom.ls.LSResourceResolver; +import org.xml.sax.EntityResolver; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; @@ -800,7 +802,11 @@ public class Jaxb2Marshaller implements MimeMarshaller, MimeUnmarshaller, Generi if (xmlReader == null) { xmlReader = XMLReaderFactory.createXMLReader(); } - xmlReader.setFeature("http://xml.org/sax/features/external-general-entities", isProcessExternalEntities()); + String name = "http://xml.org/sax/features/external-general-entities"; + xmlReader.setFeature(name, isProcessExternalEntities()); + if (!isProcessExternalEntities()) { + xmlReader.setEntityResolver(NO_OP_ENTITY_RESOLVER); + } return new SAXSource(xmlReader, inputSource); } catch (SAXException ex) { @@ -1009,4 +1015,12 @@ public class Jaxb2Marshaller implements MimeMarshaller, MimeUnmarshaller, Generi } } + + private static final EntityResolver NO_OP_ENTITY_RESOLVER = new EntityResolver() { + @Override + public InputSource resolveEntity(String publicId, String systemId) { + return new InputSource(new StringReader("")); + } + }; + } diff --git a/spring-oxm/src/main/java/org/springframework/oxm/support/AbstractMarshaller.java b/spring-oxm/src/main/java/org/springframework/oxm/support/AbstractMarshaller.java index 85ccd0b611..7c090750f1 100644 --- a/spring-oxm/src/main/java/org/springframework/oxm/support/AbstractMarshaller.java +++ b/spring-oxm/src/main/java/org/springframework/oxm/support/AbstractMarshaller.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; +import java.io.StringReader; import java.io.Writer; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -42,6 +43,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.w3c.dom.Node; import org.xml.sax.ContentHandler; +import org.xml.sax.EntityResolver; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; @@ -134,6 +136,9 @@ public abstract class AbstractMarshaller implements Marshaller, Unmarshaller { protected XMLReader createXmlReader() throws SAXException { XMLReader xmlReader = XMLReaderFactory.createXMLReader(); xmlReader.setFeature("http://xml.org/sax/features/external-general-entities", isProcessExternalEntities()); + if (!isProcessExternalEntities()) { + xmlReader.setEntityResolver(NO_OP_ENTITY_RESOLVER); + } return xmlReader; } @@ -545,4 +550,12 @@ public abstract class AbstractMarshaller implements Marshaller, Unmarshaller { protected abstract Object unmarshalReader(Reader reader) throws XmlMappingException, IOException; + + private static final EntityResolver NO_OP_ENTITY_RESOLVER = new EntityResolver() { + @Override + public InputSource resolveEntity(String publicId, String systemId) { + return new InputSource(new StringReader("")); + } + }; + } diff --git a/spring-web/src/main/java/org/springframework/http/converter/xml/Jaxb2CollectionHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/xml/Jaxb2CollectionHttpMessageConverter.java index cfbf6b6bd9..0baba9f7ae 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/xml/Jaxb2CollectionHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/xml/Jaxb2CollectionHttpMessageConverter.java @@ -16,6 +16,7 @@ package org.springframework.http.converter.xml; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; @@ -32,6 +33,7 @@ import javax.xml.bind.Unmarshaller; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLResolver; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import javax.xml.transform.Result; @@ -226,7 +228,16 @@ public class Jaxb2CollectionHttpMessageConverter protected XMLInputFactory createXmlInputFactory() { XMLInputFactory inputFactory = XMLInputFactory.newInstance(); inputFactory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, false); + inputFactory.setXMLResolver(NO_OP_XML_RESOLVER); return inputFactory; } + + private static final XMLResolver NO_OP_XML_RESOLVER = new XMLResolver() { + @Override + public Object resolveEntity(String publicID, String systemID, String base, String ns) { + return new ByteArrayInputStream(new byte[0]); + } + }; + } diff --git a/spring-web/src/main/java/org/springframework/http/converter/xml/Jaxb2RootElementHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/xml/Jaxb2RootElementHttpMessageConverter.java index 132bce6ef4..f28e646c79 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/xml/Jaxb2RootElementHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/xml/Jaxb2RootElementHttpMessageConverter.java @@ -17,6 +17,7 @@ package org.springframework.http.converter.xml; import java.io.IOException; +import java.io.StringReader; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.bind.MarshalException; @@ -38,6 +39,7 @@ import org.springframework.http.converter.HttpMessageConversionException; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.http.converter.HttpMessageNotWritableException; import org.springframework.util.ClassUtils; +import org.xml.sax.EntityResolver; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; @@ -66,6 +68,10 @@ public class Jaxb2RootElementHttpMessageConverter extends AbstractJaxb2HttpMessa this.processExternalEntities = processExternalEntities; } + public boolean isProcessExternalEntities() { + return this.processExternalEntities; + } + @Override public boolean canRead(Class clazz, MediaType mediaType) { @@ -113,7 +119,10 @@ public class Jaxb2RootElementHttpMessageConverter extends AbstractJaxb2HttpMessa try { XMLReader xmlReader = XMLReaderFactory.createXMLReader(); String featureName = "http://xml.org/sax/features/external-general-entities"; - xmlReader.setFeature(featureName, this.processExternalEntities); + xmlReader.setFeature(featureName, isProcessExternalEntities()); + if (!isProcessExternalEntities()) { + xmlReader.setEntityResolver(NO_OP_ENTITY_RESOLVER); + } return new SAXSource(xmlReader, inputSource); } catch (SAXException ex) { @@ -148,4 +157,12 @@ public class Jaxb2RootElementHttpMessageConverter extends AbstractJaxb2HttpMessa } } + + private static final EntityResolver NO_OP_ENTITY_RESOLVER = new EntityResolver() { + @Override + public InputSource resolveEntity(String publicId, String systemId) { + return new InputSource(new StringReader("")); + } + }; + } diff --git a/spring-web/src/main/java/org/springframework/http/converter/xml/SourceHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/xml/SourceHttpMessageConverter.java index 04b9935451..4385b1958a 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/xml/SourceHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/xml/SourceHttpMessageConverter.java @@ -20,11 +20,13 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.io.StringReader; import java.util.HashSet; import java.util.Set; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; +import javax.xml.stream.XMLResolver; import javax.xml.transform.Result; import javax.xml.transform.Source; import javax.xml.transform.TransformerException; @@ -35,6 +37,7 @@ import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import org.w3c.dom.Document; +import org.xml.sax.EntityResolver; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; @@ -128,8 +131,11 @@ public class SourceHttpMessageConverter extends AbstractHttpMe DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); documentBuilderFactory.setNamespaceAware(true); documentBuilderFactory.setFeature( - "http://xml.org/sax/features/external-general-entities", this.processExternalEntities); + "http://xml.org/sax/features/external-general-entities", isProcessExternalEntities()); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); + if (!isProcessExternalEntities()) { + documentBuilder.setEntityResolver(NO_OP_ENTITY_RESOLVER); + } Document document = documentBuilder.parse(body); return new DOMSource(document); } @@ -145,8 +151,11 @@ public class SourceHttpMessageConverter extends AbstractHttpMe try { XMLReader reader = XMLReaderFactory.createXMLReader(); reader.setFeature( - "http://xml.org/sax/features/external-general-entities", this.processExternalEntities); + "http://xml.org/sax/features/external-general-entities", isProcessExternalEntities()); byte[] bytes = StreamUtils.copyToByteArray(body); + if (!isProcessExternalEntities()) { + reader.setEntityResolver(NO_OP_ENTITY_RESOLVER); + } return new SAXSource(reader, new InputSource(new ByteArrayInputStream(bytes))); } catch (SAXException ex) { @@ -211,4 +220,19 @@ public class SourceHttpMessageConverter extends AbstractHttpMe } } + + private static final EntityResolver NO_OP_ENTITY_RESOLVER = new EntityResolver() { + @Override + public InputSource resolveEntity(String publicId, String systemId) { + return new InputSource(new StringReader("")); + } + }; + + private static final XMLResolver NO_OP_XML_RESOLVER = new XMLResolver() { + @Override + public Object resolveEntity(String publicID, String systemID, String base, String ns) { + return new ByteArrayInputStream(new byte[0]); + } + }; + } diff --git a/spring-web/src/test/java/org/springframework/http/converter/xml/Jaxb2RootElementHttpMessageConverterTest.java b/spring-web/src/test/java/org/springframework/http/converter/xml/Jaxb2RootElementHttpMessageConverterTest.java index c7b5c4b115..eefdcdcd18 100644 --- a/spring-web/src/test/java/org/springframework/http/converter/xml/Jaxb2RootElementHttpMessageConverterTest.java +++ b/spring-web/src/test/java/org/springframework/http/converter/xml/Jaxb2RootElementHttpMessageConverterTest.java @@ -100,7 +100,7 @@ public class Jaxb2RootElementHttpMessageConverterTest { @Test public void readXmlRootElementExternalEntityDisabled() throws Exception { Resource external = new ClassPathResource("external.txt", getClass()); - String content = "\n" + " ]>" + " &ext;"; diff --git a/spring-web/src/test/java/org/springframework/http/converter/xml/SourceHttpMessageConverterTests.java b/spring-web/src/test/java/org/springframework/http/converter/xml/SourceHttpMessageConverterTests.java index 8f9ccf8547..55b356b6aa 100644 --- a/spring-web/src/test/java/org/springframework/http/converter/xml/SourceHttpMessageConverterTests.java +++ b/spring-web/src/test/java/org/springframework/http/converter/xml/SourceHttpMessageConverterTests.java @@ -63,7 +63,7 @@ public class SourceHttpMessageConverterTests { public void setUp() throws IOException { converter = new SourceHttpMessageConverter(); Resource external = new ClassPathResource("external.txt", getClass()); - bodyExternal = "\n" + " ]>&ext;"; }