SWS-1049 - Replace direct API usage with better defaults.

This commit is contained in:
Greg Turnquist
2018-12-07 16:20:26 -06:00
parent cd27d471b6
commit 1f05307d61
76 changed files with 851 additions and 280 deletions

View File

@@ -0,0 +1,100 @@
/*
* Copyright 2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.xml;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* @author Greg Turnquist
* @since 3.0.5
*/
public class DocumentBuilderFactoryUtils {
private static final Log log = LogFactory.getLog(DocumentBuilderFactoryUtils.class);
/**
* Build a {@link DocumentBuilderFactory} then set properties to prevent external entity access.
*
* @see DocumentBuilderFactory#newInstance()
*/
public static DocumentBuilderFactory newInstance() {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
try {
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
} catch (IllegalArgumentException e) {
if (log.isWarnEnabled()) {
log.warn(XMLConstants.ACCESS_EXTERNAL_DTD + " property not supported by " + factory.getClass().getCanonicalName());
}
}
try {
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
} catch (IllegalArgumentException e) {
if (log.isWarnEnabled()) {
log.warn(XMLConstants.ACCESS_EXTERNAL_SCHEMA + " property not supported by " + factory.getClass().getCanonicalName());
}
}
try {
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
} catch (ParserConfigurationException e) {
if (log.isWarnEnabled()) {
log.warn("FEATURE 'http://apache.org/xml/features/disallow-doctype-decl' is probably not supported by " + factory.getClass().getCanonicalName());
}
}
try {
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
} catch (ParserConfigurationException e) {
if (log.isWarnEnabled()) {
log.warn("FEATURE 'http://xml.org/sax/features/external-general-entities' is probably not supported by " + factory.getClass().getCanonicalName());
}
}
try {
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
} catch (ParserConfigurationException e) {
if (log.isWarnEnabled()) {
log.warn("FEATURE 'http://xml.org/sax/features/external-parameter-entities' is probably not supported by " + factory.getClass().getCanonicalName());
}
}
try {
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
} catch (ParserConfigurationException e) {
if (log.isWarnEnabled()) {
log.warn("FEATURE 'http://apache.org/xml/features/nonvalidating/load-external-dtd' is probably not supported by " + factory.getClass().getCanonicalName());
}
}
try {
factory.setXIncludeAware(false);
factory.setExpandEntityReferences(false);
} catch (Exception e) {
if (log.isWarnEnabled()) {
log.warn("Caught " + e.getMessage() + " attempting to configure your XML parser.");
}
}
return factory;
}
}

View File

@@ -22,12 +22,11 @@ import org.springframework.util.ClassUtils;
* Helper class used to find the current version of JAXP. We cannot depend on the Java version, since JAXP can be
* upgraded independently of the Java version.
*
* <p>Only distinguishes between JAXP 1.0, 1.1, 1.3, and 1.4, since JAXP 1.2 was a maintenance release with no new
* <p>Only distinguishes between JAXP 1.0, 1.1, 1.3, 1.4, and 1.5, since JAXP 1.2 was a maintenance release with no new
* classes.
*
* <p>Note that Spring-WS requires JDK 1.5 as of Spring-WS 2.0, and therefore has at least JAXP 1.3 available.
*
* @author Arjen Poutsma
* @author Greg Turnquist
* @since 1.0.0
*/
public abstract class JaxpVersion {
@@ -52,19 +51,31 @@ public abstract class JaxpVersion {
*/
public static final int JAXP_14 = 4;
/**
* Constant identifying JAXP 1.5.
*/
public static final int JAXP_15 = 5;
private static final String JAXP_14_CLASS_NAME = "javax.xml.transform.stax.StAXSource";
private static final String JAXP_15_CLASS_NAME = "javax.xml.validation.SchemaFactoryConfigurationError";
private static int jaxpVersion;
static {
ClassLoader classLoader = JaxpVersion.class.getClassLoader();
try {
ClassUtils.forName(JAXP_14_CLASS_NAME, classLoader);
jaxpVersion = JAXP_14;
}
catch (ClassNotFoundException ex) {
// leave 1.3 as default (it's either 1.3 or unknown)
jaxpVersion = JAXP_13;
ClassUtils.forName(JAXP_15_CLASS_NAME, classLoader);
jaxpVersion = JAXP_15;
} catch (ClassNotFoundException ex) {
try {
ClassUtils.forName(JAXP_14_CLASS_NAME, classLoader);
jaxpVersion = JAXP_14;
} catch (ClassNotFoundException e) {
// leave 1.3 as default (it's either 1.3 or unknown)
jaxpVersion = JAXP_13;
}
}
}
@@ -76,6 +87,7 @@ public abstract class JaxpVersion {
* @see #JAXP_11
* @see #JAXP_13
* @see #JAXP_14
* @see #JAXP_15
*/
public static int getJaxpVersion() {
return jaxpVersion;

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.xml;
import javax.xml.stream.XMLInputFactory;
/**
* @author Greg Turnquist
*/
public class XMLInputFactoryUtils {
/**
* Build an {@link XMLInputFactory} and set properties to prevent external entities from accessing.
*
* @see XMLInputFactory#newInstance()
*/
public static XMLInputFactory newInstance() {
XMLInputFactory factory = XMLInputFactory.newInstance();
factory.setProperty(XMLInputFactory.SUPPORT_DTD, false);
factory.setProperty("javax.xml.stream.isSupportingExternalEntities", false);
return factory;
}
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright 2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.xml.transform;
import javax.xml.XMLConstants;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
/**
* @author Greg Turnquist
* @since 3.0.5
*/
public class TransformerFactoryUtils {
/**
* Build a new {@link TransformerFactory} using the default constructor.
*/
public static TransformerFactory newInstance() {
return defaultSettings(TransformerFactory.newInstance());
}
/**
* Build an {@link TransformerFactory} and prevent external entities from accessing.
*
* @see TransformerFactory#newInstance()
*/
public static TransformerFactory newInstance(Class<? extends TransformerFactory> transformerFactoryClass) {
try {
return defaultSettings(transformerFactoryClass.newInstance());
} catch (InstantiationException | IllegalAccessException e) {
throw new TransformerFactoryConfigurationError(e,
"Could not instantiate TransformerFactory [" + transformerFactoryClass + "]");
}
}
/**
* Prevent external entities from accessing.
*/
private static TransformerFactory defaultSettings(TransformerFactory factory) {
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
return factory;
}
}

View File

@@ -22,7 +22,6 @@ import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import org.springframework.util.Assert;
@@ -83,16 +82,11 @@ public class TransformerHelper {
*/
protected TransformerFactory newTransformerFactory(Class<? extends TransformerFactory> transformerFactoryClass) {
if (transformerFactoryClass != null) {
try {
return transformerFactoryClass.newInstance();
}
catch (Exception ex) {
throw new TransformerFactoryConfigurationError(ex,
"Could not instantiate TransformerFactory [" + transformerFactoryClass + "]");
}
}
else {
return TransformerFactory.newInstance();
TransformerFactory transformerFactory = TransformerFactoryUtils.newInstance(transformerFactoryClass);
return transformerFactory;
} else {
TransformerFactory transformerFactory = TransformerFactoryUtils.newInstance();
return transformerFactory;
}
}

View File

@@ -32,6 +32,7 @@ import org.springframework.core.io.Resource;
* Internal class that uses JAXP 1.0 features to create {@code XmlValidator} instances.
*
* @author Arjen Poutsma
* @author Greg Turnquist
* @since 1.0.0
*/
abstract class Jaxp13ValidatorFactory {

View File

@@ -0,0 +1,130 @@
/*
* Copyright 2005-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.xml.validation;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.xml.XMLConstants;
import javax.xml.transform.Source;
import javax.xml.validation.Schema;
import javax.xml.validation.Validator;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
import org.xml.sax.SAXParseException;
import org.springframework.core.io.Resource;
/**
* Internal class that uses JAXP 1.5 features to create an {@code XmlValidator} with settings to prevent
* external entity access.
*
* @author Arjen Poutsma
* @author Greg Turnquist
* @since 3.0.5
*/
abstract class Jaxp15ValidatorFactory {
private static final Log log = LogFactory.getLog(Jaxp15ValidatorFactory.class);
static XmlValidator createValidator(Resource[] resources, String schemaLanguage) throws IOException {
try {
Schema schema = SchemaLoaderUtils.loadSchema(resources, schemaLanguage);
return new Jaxp15Validator(schema);
}
catch (SAXException ex) {
throw new XmlValidationException("Could not create Schema: " + ex.getMessage(), ex);
}
}
private static class Jaxp15Validator implements XmlValidator {
private Schema schema;
public Jaxp15Validator(Schema schema) {
this.schema = schema;
}
@Override
public SAXParseException[] validate(Source source) throws IOException {
return validate(source, null);
}
@Override
public SAXParseException[] validate(Source source, ValidationErrorHandler errorHandler) throws IOException {
if (errorHandler == null) {
errorHandler = new DefaultValidationErrorHandler();
}
Validator validator = schema.newValidator();
try {
validator.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
} catch (SAXNotRecognizedException | SAXNotSupportedException e) {
if (log.isWarnEnabled()) {
log.warn(XMLConstants.ACCESS_EXTERNAL_DTD + " property not supported by " + validator.getClass().getCanonicalName());
}
}
try {
validator.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
} catch (SAXNotRecognizedException | SAXNotSupportedException e) {
if (log.isWarnEnabled()) {
log.warn(XMLConstants.ACCESS_EXTERNAL_SCHEMA + " property not supported by " + validator.getClass().getCanonicalName());
}
}
validator.setErrorHandler(errorHandler);
try {
validator.validate(source);
return errorHandler.getErrors();
}
catch (SAXException ex) {
throw new XmlValidationException("Could not validate source: " + ex.getMessage(), ex);
}
}
}
/** {@code ErrorHandler} implementation that stores errors and fatal errors in a list. */
private static class DefaultValidationErrorHandler implements ValidationErrorHandler {
private List<SAXParseException> errors = new ArrayList<SAXParseException>();
@Override
public SAXParseException[] getErrors() {
return errors.toArray(new SAXParseException[errors.size()]);
}
@Override
public void warning(SAXParseException ex) throws SAXException {
}
@Override
public void error(SAXParseException ex) throws SAXException {
errors.add(ex);
}
@Override
public void fatalError(SAXParseException ex) throws SAXException {
errors.add(ex);
}
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright 2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.xml.validation;
import javax.xml.XMLConstants;
import javax.xml.validation.SchemaFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
/**
* @author Greg Turnquist
* @since 3.0.5
*/
public class SchemaFactoryUtils {
private static final Log log = LogFactory.getLog(SchemaFactoryUtils.class);
/**
* Build a {@link SchemaFactory} and set properties to prevent external entities from accessing.
*
* @see SchemaFactory#newInstance(String)
*/
public static SchemaFactory newInstance(String schemaLanguage) {
SchemaFactory schemaFactory = SchemaFactory.newInstance(schemaLanguage);
try {
schemaFactory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
} catch (SAXNotRecognizedException | SAXNotSupportedException e) {
if (log.isWarnEnabled()) {
log.warn(XMLConstants.ACCESS_EXTERNAL_DTD + " property not supported by " + schemaFactory.getClass().getCanonicalName());
}
}
try {
schemaFactory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "file,jar:file");
} catch (SAXNotRecognizedException | SAXNotSupportedException e) {
if (log.isWarnEnabled()) {
log.warn(XMLConstants.ACCESS_EXTERNAL_SCHEMA + " property not supported by " + schemaFactory.getClass().getCanonicalName());
}
}
return schemaFactory;
}
}

View File

@@ -21,14 +21,13 @@ import javax.xml.transform.Source;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
import org.springframework.xml.transform.ResourceSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;
/**
* Convenient utility methods for loading of {@link Schema} objects, performing standard handling of input streams.
*
@@ -67,14 +66,14 @@ public abstract class SchemaLoaderUtils {
Assert.notEmpty(resources, "No resources given");
Assert.hasLength(schemaLanguage, "No schema language provided");
Source[] schemaSources = new Source[resources.length];
XMLReader xmlReader = XMLReaderFactory.createXMLReader();
XMLReader xmlReader = XMLReaderFactoryUtils.createXMLReader();
xmlReader.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
for (int i = 0; i < resources.length; i++) {
Assert.notNull(resources[i], "Resource is null");
Assert.isTrue(resources[i].exists(), "Resource " + resources[i] + " does not exist");
schemaSources[i] = new ResourceSource(xmlReader, resources[i]);
}
SchemaFactory schemaFactory = SchemaFactory.newInstance(schemaLanguage);
SchemaFactory schemaFactory = SchemaFactoryUtils.newInstance(schemaLanguage);
return schemaFactory.newSchema(schemaSources);
}
@@ -87,4 +86,5 @@ public abstract class SchemaLoaderUtils {
return null;
}
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.xml.validation;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;
/**
* @author Greg Turnquist
* @since 3.0.5
*/
public class XMLReaderFactoryUtils {
/**
* Build a {@link XMLReader} and set properties to prevent external entity access.
*
* @see XMLReaderFactory#createXMLReader()
*/
public static XMLReader createXMLReader() throws SAXException {
XMLReader xmlReader = XMLReaderFactory.createXMLReader();
xmlReader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
xmlReader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
xmlReader.setFeature("http://xml.org/sax/features/external-general-entities", false);
xmlReader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
return xmlReader;
}
}

View File

@@ -19,13 +19,13 @@ package org.springframework.xml.validation;
import java.io.IOException;
import javax.xml.validation.Validator;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
import org.springframework.xml.JaxpVersion;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Factory for {@link XmlValidator} objects, being aware of JAXP 1.3 {@link Validator}s, and JAXP 1.0 parsing
* capabilities. Mainly for internal use within the framework.
@@ -84,10 +84,15 @@ public abstract class XmlValidatorFactory {
Assert.hasLength(schemaLanguage, "No schema language provided");
Assert.isTrue(SCHEMA_W3C_XML.equals(schemaLanguage) || SCHEMA_RELAX_NG.equals(schemaLanguage),
"Invalid schema language: " + schemaLanguage);
Assert.noNullElements(schemaResources, "No null schemaResources allowed");
for (Resource schemaResource : schemaResources) {
Assert.isTrue(schemaResource.exists(), "schema [" + schemaResource + "] does not exist");
}
if (JaxpVersion.getJaxpVersion() >= JaxpVersion.JAXP_13) {
if (JaxpVersion.getJaxpVersion() >= JaxpVersion.JAXP_15) {
logger.trace("Creating JAXP 1.5 XmlValidator");
return Jaxp15ValidatorFactory.createValidator(schemaResources, schemaLanguage);
}
else if (JaxpVersion.getJaxpVersion() >= JaxpVersion.JAXP_13) {
logger.trace("Creating JAXP 1.3 XmlValidator");
return Jaxp13ValidatorFactory.createValidator(schemaResources, schemaLanguage);
}

View File

@@ -31,6 +31,7 @@ import org.xml.sax.SAXException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
import org.springframework.xml.DocumentBuilderFactoryUtils;
import org.springframework.xml.sax.SaxUtils;
import org.springframework.xml.validation.XmlValidator;
import org.springframework.xml.validation.XmlValidatorFactory;
@@ -48,7 +49,7 @@ import org.springframework.xml.validation.XmlValidatorFactory;
*/
public class SimpleXsdSchema implements XsdSchema, InitializingBean {
private static DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
private static DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactoryUtils.newInstance();
private static final String SCHEMA_NAMESPACE = "http://www.w3.org/2001/XMLSchema";