Consistently ignore exceptions for Xerces-specific properties

Closes gh-36682

(cherry picked from commit af7a5716e8)
This commit is contained in:
Juergen Hoeller
2026-04-21 20:34:29 +02:00
parent 515152b645
commit cba8f225d9
4 changed files with 140 additions and 64 deletions
@@ -913,17 +913,29 @@ public class Jaxb2Marshaller implements MimeMarshaller, MimeUnmarshaller, Generi
// By default, Spring will prevent the processing of external entities.
// This is a mitigation against XXE attacks.
if (xmlReader == null) {
SAXParserFactory saxParserFactory = this.sourceParserFactory;
if (saxParserFactory == null) {
saxParserFactory = SAXParserFactory.newInstance();
saxParserFactory.setNamespaceAware(true);
saxParserFactory.setFeature(
"http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd());
saxParserFactory.setFeature(
"http://xml.org/sax/features/external-general-entities", isProcessExternalEntities());
this.sourceParserFactory = saxParserFactory;
SAXParserFactory factory = this.sourceParserFactory;
if (factory == null) {
factory = SAXParserFactory.newInstance();
factory.setNamespaceAware(true);
try {
factory.setFeature(
"http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd());
}
catch (Exception ex) {
// Xerces properties not recognized/supported - ignore
}
try {
factory.setFeature(
"http://xml.org/sax/features/external-general-entities", isProcessExternalEntities());
factory.setFeature(
"http://xml.org/sax/features/external-parameter-entities", isProcessExternalEntities());
}
catch (Exception ex) {
// SAX properties not recognized/supported - ignore
}
this.sourceParserFactory = factory;
}
SAXParser saxParser = saxParserFactory.newSAXParser();
SAXParser saxParser = factory.newSAXParser();
xmlReader = saxParser.getXMLReader();
}
if (!isProcessExternalEntities()) {
@@ -931,8 +943,8 @@ public class Jaxb2Marshaller implements MimeMarshaller, MimeUnmarshaller, Generi
}
return new SAXSource(xmlReader, inputSource);
}
catch (SAXException | ParserConfigurationException ex) {
logger.info("Processing of external entities could not be disabled", ex);
catch (Exception ex) {
logger.warn("Processing of external entities could not be disabled", ex);
return source;
}
}
@@ -75,6 +75,7 @@ public abstract class AbstractMarshaller implements Marshaller, Unmarshaller {
private static final EntityResolver NO_OP_ENTITY_RESOLVER =
(publicId, systemId) -> new InputSource(new StringReader(""));
/** Logger available to subclasses. */
protected final Log logger = LogFactory.getLog(getClass());
@@ -167,8 +168,22 @@ public abstract class AbstractMarshaller implements Marshaller, Unmarshaller {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(false);
factory.setNamespaceAware(true);
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd());
factory.setFeature("http://xml.org/sax/features/external-general-entities", isProcessExternalEntities());
try {
factory.setFeature(
"http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd());
}
catch (Exception ex) {
// Xerces properties not recognized/supported - ignore
}
try {
factory.setFeature(
"http://xml.org/sax/features/external-general-entities", isProcessExternalEntities());
factory.setFeature(
"http://xml.org/sax/features/external-parameter-entities", isProcessExternalEntities());
}
catch (Exception ex) {
// SAX properties not recognized/supported - ignore
}
return factory;
}
@@ -197,17 +212,29 @@ public abstract class AbstractMarshaller implements Marshaller, Unmarshaller {
* @throws ParserConfigurationException if thrown by JAXP methods
*/
protected XMLReader createXmlReader() throws SAXException, ParserConfigurationException {
SAXParserFactory parserFactory = this.saxParserFactory;
if (parserFactory == null) {
parserFactory = SAXParserFactory.newInstance();
parserFactory.setNamespaceAware(true);
parserFactory.setFeature(
"http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd());
parserFactory.setFeature(
"http://xml.org/sax/features/external-general-entities", isProcessExternalEntities());
this.saxParserFactory = parserFactory;
SAXParserFactory factory = this.saxParserFactory;
if (factory == null) {
factory = SAXParserFactory.newInstance();
factory.setNamespaceAware(true);
try {
factory.setFeature(
"http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd());
}
catch (Exception ex) {
// Xerces properties not recognized/supported - ignore
}
try {
factory.setFeature(
"http://xml.org/sax/features/external-general-entities", isProcessExternalEntities());
factory.setFeature(
"http://xml.org/sax/features/external-parameter-entities", isProcessExternalEntities());
}
catch (Exception ex) {
// SAX properties not recognized/supported - ignore
}
this.saxParserFactory = factory;
}
SAXParser saxParser = parserFactory.newSAXParser();
SAXParser saxParser = factory.newSAXParser();
XMLReader xmlReader = saxParser.getXMLReader();
if (!isProcessExternalEntities()) {
xmlReader.setEntityResolver(NO_OP_ENTITY_RESOLVER);
@@ -459,8 +486,7 @@ public abstract class AbstractMarshaller implements Marshaller, Unmarshaller {
catch (NullPointerException ex) {
if (!isSupportDtd()) {
throw new UnmarshallingFailureException("NPE while unmarshalling. " +
"This can happen on JDK 1.6 due to the presence of DTD " +
"declarations, which are disabled.");
"This can happen due to the presence of DTD declarations, which are disabled.");
}
throw ex;
}
@@ -19,7 +19,6 @@ package org.springframework.http.converter.xml;
import java.io.StringReader;
import java.nio.charset.Charset;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.Result;
@@ -38,7 +37,6 @@ import jakarta.xml.bind.annotation.XmlRootElement;
import jakarta.xml.bind.annotation.XmlType;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.springframework.core.annotation.AnnotationUtils;
@@ -68,6 +66,10 @@ import org.springframework.util.ClassUtils;
*/
public class Jaxb2RootElementHttpMessageConverter extends AbstractJaxb2HttpMessageConverter<Object> {
private static final EntityResolver NO_OP_ENTITY_RESOLVER =
(publicId, systemId) -> new InputSource(new StringReader(""));
private boolean supportDtd = false;
private boolean processExternalEntities = false;
@@ -177,24 +179,36 @@ public class Jaxb2RootElementHttpMessageConverter extends AbstractJaxb2HttpMessa
try {
// By default, Spring will prevent the processing of external entities.
// This is a mitigation against XXE attacks.
SAXParserFactory saxParserFactory = this.sourceParserFactory;
if (saxParserFactory == null) {
saxParserFactory = SAXParserFactory.newInstance();
saxParserFactory.setNamespaceAware(true);
saxParserFactory.setFeature(
"http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd());
saxParserFactory.setFeature(
"http://xml.org/sax/features/external-general-entities", isProcessExternalEntities());
this.sourceParserFactory = saxParserFactory;
SAXParserFactory factory = this.sourceParserFactory;
if (factory == null) {
factory = SAXParserFactory.newInstance();
factory.setNamespaceAware(true);
try {
factory.setFeature(
"http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd());
}
catch (Exception ex) {
// Xerces properties not recognized/supported - ignore
}
try {
factory.setFeature(
"http://xml.org/sax/features/external-general-entities", isProcessExternalEntities());
factory.setFeature(
"http://xml.org/sax/features/external-parameter-entities", isProcessExternalEntities());
}
catch (Exception ex) {
// SAX properties not recognized/supported - ignore
}
this.sourceParserFactory = factory;
}
SAXParser saxParser = saxParserFactory.newSAXParser();
SAXParser saxParser = factory.newSAXParser();
XMLReader xmlReader = saxParser.getXMLReader();
if (!isProcessExternalEntities()) {
xmlReader.setEntityResolver(NO_OP_ENTITY_RESOLVER);
}
return new SAXSource(xmlReader, inputSource);
}
catch (SAXException | ParserConfigurationException ex) {
catch (Exception ex) {
logger.warn("Processing of external entities could not be disabled", ex);
return source;
}
@@ -240,8 +254,4 @@ public class Jaxb2RootElementHttpMessageConverter extends AbstractJaxb2HttpMessa
return true;
}
private static final EntityResolver NO_OP_ENTITY_RESOLVER =
(publicId, systemId) -> new InputSource(new StringReader(""));
}
@@ -179,17 +179,29 @@ public class SourceHttpMessageConverter<T extends Source> extends AbstractHttpMe
try {
// By default, Spring will prevent the processing of external entities.
// This is a mitigation against XXE attacks.
DocumentBuilderFactory builderFactory = this.documentBuilderFactory;
if (builderFactory == null) {
builderFactory = DocumentBuilderFactory.newInstance();
builderFactory.setNamespaceAware(true);
builderFactory.setFeature(
"http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd());
builderFactory.setFeature(
"http://xml.org/sax/features/external-general-entities", isProcessExternalEntities());
this.documentBuilderFactory = builderFactory;
DocumentBuilderFactory factory = this.documentBuilderFactory;
if (factory == null) {
factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
try {
factory.setFeature(
"http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd());
}
catch (Exception ex) {
// Xerces properties not recognized/supported - ignore
}
try {
factory.setFeature(
"http://xml.org/sax/features/external-general-entities", isProcessExternalEntities());
factory.setFeature(
"http://xml.org/sax/features/external-parameter-entities", isProcessExternalEntities());
}
catch (Exception ex) {
// SAX properties not recognized/supported - ignore
}
this.documentBuilderFactory = factory;
}
DocumentBuilder builder = builderFactory.newDocumentBuilder();
DocumentBuilder builder = factory.newDocumentBuilder();
if (!isProcessExternalEntities()) {
builder.setEntityResolver(NO_OP_ENTITY_RESOLVER);
}
@@ -215,17 +227,29 @@ public class SourceHttpMessageConverter<T extends Source> extends AbstractHttpMe
private SAXSource readSAXSource(InputStream body, HttpInputMessage inputMessage) throws IOException {
try {
SAXParserFactory parserFactory = this.saxParserFactory;
if (parserFactory == null) {
parserFactory = SAXParserFactory.newInstance();
parserFactory.setNamespaceAware(true);
parserFactory.setFeature(
"http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd());
parserFactory.setFeature(
"http://xml.org/sax/features/external-general-entities", isProcessExternalEntities());
this.saxParserFactory = parserFactory;
SAXParserFactory factory = this.saxParserFactory;
if (factory == null) {
factory = SAXParserFactory.newInstance();
factory.setNamespaceAware(true);
try {
factory.setFeature(
"http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd());
}
catch (Exception ex) {
// Xerces properties not recognized/supported - ignore
}
try {
factory.setFeature(
"http://xml.org/sax/features/external-general-entities", isProcessExternalEntities());
factory.setFeature(
"http://xml.org/sax/features/external-parameter-entities", isProcessExternalEntities());
}
catch (Exception ex) {
// SAX properties not recognized/supported - ignore
}
this.saxParserFactory = factory;
}
SAXParser saxParser = parserFactory.newSAXParser();
SAXParser saxParser = factory.newSAXParser();
XMLReader xmlReader = saxParser.getXMLReader();
if (!isProcessExternalEntities()) {
xmlReader.setEntityResolver(NO_OP_ENTITY_RESOLVER);
@@ -233,7 +257,11 @@ public class SourceHttpMessageConverter<T extends Source> extends AbstractHttpMe
byte[] bytes = StreamUtils.copyToByteArray(body);
return new SAXSource(xmlReader, new InputSource(new ByteArrayInputStream(bytes)));
}
catch (SAXException | ParserConfigurationException ex) {
catch (ParserConfigurationException ex) {
throw new HttpMessageNotReadableException(
"Could not set feature: " + ex.getMessage(), ex, inputMessage);
}
catch (SAXException ex) {
throw new HttpMessageNotReadableException(
"Could not parse document: " + ex.getMessage(), ex, inputMessage);
}