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 ae9a0af5ee4..07f5fa10880 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; @@ -72,6 +73,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; @@ -806,7 +808,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) { @@ -1019,4 +1025,11 @@ 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 8ab74248603..1e8402ee0d0 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; @@ -43,6 +44,7 @@ import org.apache.commons.logging.LogFactory; import org.w3c.dom.Document; 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; @@ -156,6 +158,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 81fb9373e3f..c5834c5e50d 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; @@ -230,7 +232,16 @@ public class Jaxb2CollectionHttpMessageConverter protected XMLInputFactory createXmlInputFactory() { XMLInputFactory inputFactory = XMLInputFactory.newInstance(); inputFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, 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 6c1b0c7913b..879cdb8845d 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; @@ -67,6 +69,10 @@ public class Jaxb2RootElementHttpMessageConverter extends AbstractJaxb2HttpMessa this.processExternalEntities = processExternalEntities; } + public boolean isProcessExternalEntities() { + return this.processExternalEntities; + } + @Override public boolean canRead(Class clazz, MediaType mediaType) { return (clazz.isAnnotationPresent(XmlRootElement.class) || clazz.isAnnotationPresent(XmlType.class)) && @@ -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 ec7daec5fc2..6a39a2b9081 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,12 +20,14 @@ 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.XMLInputFactory; +import javax.xml.stream.XMLResolver; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import javax.xml.transform.Result; @@ -39,6 +41,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; @@ -136,8 +139,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); } @@ -152,9 +158,11 @@ public class SourceHttpMessageConverter extends AbstractHttpMe private SAXSource readSAXSource(InputStream body) throws IOException { try { XMLReader reader = XMLReaderFactory.createXMLReader(); - reader.setFeature( - "http://xml.org/sax/features/external-general-entities", this.processExternalEntities); + reader.setFeature("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) { @@ -165,7 +173,10 @@ public class SourceHttpMessageConverter extends AbstractHttpMe private Source readStAXSource(InputStream body) { try { XMLInputFactory inputFactory = XMLInputFactory.newFactory(); - inputFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, this.processExternalEntities); + inputFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, isProcessExternalEntities()); + if (!isProcessExternalEntities()) { + inputFactory.setXMLResolver(NO_OP_XML_RESOLVER); + } XMLStreamReader streamReader = inputFactory.createXMLStreamReader(body); return new StAXSource(streamReader); } @@ -231,4 +242,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/Jaxb2RootElementHttpMessageConverterTests.java b/spring-web/src/test/java/org/springframework/http/converter/xml/Jaxb2RootElementHttpMessageConverterTests.java index a4fcd106eef..c1f1ec1a8de 100644 --- a/spring-web/src/test/java/org/springframework/http/converter/xml/Jaxb2RootElementHttpMessageConverterTests.java +++ b/spring-web/src/test/java/org/springframework/http/converter/xml/Jaxb2RootElementHttpMessageConverterTests.java @@ -110,7 +110,7 @@ public class Jaxb2RootElementHttpMessageConverterTests { @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 51288cabb32..6ad8746c215 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 @@ -66,7 +66,7 @@ public class SourceHttpMessageConverterTests { converter = new SourceHttpMessageConverter(); Resource external = new ClassPathResource("external.txt", getClass()); - bodyExternal = "\n" + " ]>&ext;"; }