Skip to content

Commit

Permalink
EntityResolver for DocumentFactory [GEOT-5514]
Browse files Browse the repository at this point in the history
  • Loading branch information
jodygarnett authored and aaime committed Sep 19, 2016
1 parent 8ffee63 commit cda7263
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 117 deletions.
Expand Up @@ -26,22 +26,31 @@
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.geotools.xml.handlers.DocumentHandler;
import org.geotools.xml.schema.Schema;
import org.xml.sax.SAXException;

/**
* This is the main entry point into the XSI parsing routines.
* <p>
* Example Use:
*
* <pre>
* Object x = DocumentFactory.getInstance(new URI(&quot;MyInstanceDocumentURI&quot;);
* </pre>
*
* </p>
* <p>
* A selection of the hints available to configure parsing:
* </p>
* <ul>
* <li>{@link #VALIDATION_HINT} - Boolean.FALSE to disable validation</li>
* <li>{@link DocumentHandler#DEFAULT_NAMESPACE_HINT_KEY} - {@link Schema} for parsing and validation</li>
* <li>{@link XMLHandlerHints#FLOW_HANDLER_HINT}</li>
* <li>{@link XMLHandlerHints#NAMESPACE_MAPPING} - Map&lt;String,URL&gt; namespace mapping</li>
* <li>{@link XMLHandlerHints#ENTITY_RESOLVER} - control entry resolution<li>
* <li>{@link #DISABLE_EXTERNAL_ENTITIES} - Boolean.TRUE to disable entity resolution<li>
* </ul>
*
* @author dzwiers, Refractions Research, Inc. http://www.refractions.net
* @author $Author:$ (last modification)
*
*
* @source $URL$
* http://svn.osgeo.org/geotools/trunk/modules/library/xml/src/main/java/org/geotools/xml
Expand All @@ -56,6 +65,13 @@ public class DocumentFactory {
* the resulting objects is weekend by turning this param to false.
*/
public static final String VALIDATION_HINT = "DocumentFactory_VALIDATION_HINT";

/**
* When this hint is contained and set to Boolean.TRUE, external entities will be disabled. This
* setting is used to alivate XXE attacks, preventing both {@link #VALIDATION_HINT} and
* {@link XMLHandlerHints#ENTITY_RESOLVER} from being effective.
*/
public static final String DISABLE_EXTERNAL_ENTITIES = "DocumentFactory_DISABLE_EXTERNAL_ENTITIES";

/**
* <p>
Expand All @@ -72,8 +88,8 @@ public class DocumentFactory {
*
* @see DocumentFactory#getInstance(URI, Map, Level)
*/
public static Object getInstance(URI desiredDocument, Map hints) throws SAXException {
return getInstance(desiredDocument, hints, Level.WARNING, false);
public static Object getInstance(URI desiredDocument, Map<String,Object> hints) throws SAXException {
return getInstance(desiredDocument, hints, Level.WARNING);
}

/**
Expand All @@ -94,31 +110,10 @@ public static Object getInstance(URI desiredDocument, Map hints) throws SAXExcep
*
* @see DocumentFactory#getInstance(URI, Map, Level, boolean)
*/
public static Object getInstance(URI desiredDocument, Map hints, Level level)
public static Object getInstance(URI desiredDocument, @SuppressWarnings("rawtypes") Map hints, Level level)
throws SAXException {
return getInstance(desiredDocument, hints, level, false);
}

/**
* <p>
* Parses the instance data provided. This method assumes that the XML document is fully
* described using XML Schemas. Failure to be fully described as Schemas will result in errors,
* as opposed to a vid parse.
* </p>
*
* @param desiredDocument
* @param hints
* May be null.
* @param level
* @param parseExternalEntities
*
* @return Object
*
* @throws SAXException
*/
public static Object getInstance(URI desiredDocument, Map hints, Level level,
boolean parseExternalEntities) throws SAXException {
SAXParser parser = getParser(parseExternalEntities);
@SuppressWarnings("unchecked")
SAXParser parser = getParser(hints);

XMLSAXHandler xmlContentHandler = new XMLSAXHandler(desiredDocument, hints);
XMLSAXHandler.setLogLevel(level);
Expand Down Expand Up @@ -150,30 +145,8 @@ public static Object getInstance(URI desiredDocument, Map hints, Level level,
*
* @see DocumentFactory#getInstance(InputStream, Map, Level, boolean)
*/
public static Object getInstance(InputStream is, Map hints, Level level) throws SAXException {
return getInstance(is, hints, level, false);
}

/**
* <p>
* Parses the instance data provided. This method assumes that the XML document is fully
* described using XML Schemas. Failure to be fully described as Schemas will result in errors,
* as opposed to a vid parse.
* </p>
*
* @param is
* @param hints
* May be null.
* @param level
* @param parseExternalEntities
*
* @return Object
*
* @throws SAXException
*/
public static Object getInstance(InputStream is, Map hints, Level level,
boolean parseExternalEntities) throws SAXException {
SAXParser parser = getParser(parseExternalEntities);
public static Object getInstance(InputStream is, Map<String,Object> hints, Level level) throws SAXException {
SAXParser parser = getParser(hints);

XMLSAXHandler xmlContentHandler = new XMLSAXHandler(hints);
XMLSAXHandler.setLogLevel(level);
Expand All @@ -191,19 +164,22 @@ public static Object getInstance(InputStream is, Map hints, Level level,
/*
* Convenience method to create an instance of a SAXParser if it is null.
*/
private static SAXParser getParser(boolean parseExternalEntities) throws SAXException {
private static SAXParser getParser(Map<String,Object> hints) throws SAXException {
SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setNamespaceAware(true);
spf.setValidating(false);

try {
if(!parseExternalEntities) {
if (hints != null && hints.containsKey(DISABLE_EXTERNAL_ENTITIES)
&& Boolean.TRUE.equals(hints.get(DISABLE_EXTERNAL_ENTITIES))) {
// The following configuration prevents XML External Entity Injection (XXE) attacks
// See for more information:
// https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Processing
spf.setFeature("http://xml.org/sax/features/external-general-entities", false);
spf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
}
// This is an XML Schema driven parser, no DTD required (XMLSaxHandler will reject all dtd references)
spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
spf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);

SAXParser sp = spf.newSAXParser();
return sp;
Expand Down
Expand Up @@ -30,7 +30,7 @@
*
* @source $URL$
*/
public class XMLHandlerHints implements Map {
public class XMLHandlerHints implements Map<String,Object> {

/**
* Declares the schemas to use for parsing.
Expand All @@ -44,6 +44,8 @@ public class XMLHandlerHints implements Map {
public static final String STREAM_HINT = "org.geotools.xml.gml.STREAM_HINT";
/** Sets the level of compliance that the filter encoder should use */
public static final String FILTER_COMPLIANCE_STRICTNESS = "org.geotools.xml.filter.FILTER_COMPLIANCE_STRICTNESS";
/** Supplied {@link EntityResolver} for Schema and/or DTD validation */
public final static String ENTITY_RESOLVER ="org.xml.sax.EntityResolver";
/**
* The value so that the parser will encode all Geotools filters with no modifications.
*/
Expand Down Expand Up @@ -119,7 +121,7 @@ public class XMLHandlerHints implements Map {
public static final Integer VALUE_FILTER_COMPLIANCE_HIGH = new Integer(2);


private Map map=new HashMap();
private Map<String,Object> map=new HashMap<String,Object>();
public void clear() {
map.clear();
}
Expand All @@ -132,7 +134,7 @@ public boolean containsValue( Object value ) {
return map.containsValue(value);
}

public Set entrySet() {
public Set<Entry<String,Object>> entrySet() {
return map.entrySet();
}

Expand All @@ -152,16 +154,16 @@ public boolean isEmpty() {
return map.isEmpty();
}

public Set keySet() {
public Set<String> keySet() {
return map.keySet();
}

public Object put( Object arg0, Object arg1 ) {
return map.put(arg0, arg1);
public Object put( String key, Object value ) {
return map.put(key, value);
}

public void putAll( Map arg0 ) {
map.putAll(arg0);
public void putAll( Map<? extends String,? extends Object> other ) {
map.putAll(other);
}

public Object remove( Object key ) {
Expand All @@ -172,7 +174,7 @@ public int size() {
return map.size();
}

public Collection values() {
public Collection<Object> values() {
return map.values();
}

Expand Down
Expand Up @@ -20,6 +20,7 @@
import java.io.StringReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
Expand All @@ -33,6 +34,7 @@
import org.geotools.xml.handlers.ElementHandlerFactory;
import org.geotools.xml.handlers.IgnoreHandler;
import org.xml.sax.Attributes;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
Expand All @@ -56,10 +58,13 @@
* periodically check it to see if it should stop parsing. See the FlowHandler
* interface.
* </p>
*
* <p>
* This is an XML Schema driven parser and {@link #resolveEntity(String, String)} will
* ignore all dtd references. If an {@link EntityResolver} is provided it will be used.
* </p>
*
* @author dzwiers, Refractions Research, Inc. http://www.refractions.net
* @author $Author:$ (last modification)
*
*
* @source $URL$
* @version $Id$
Expand All @@ -82,40 +87,41 @@ public class XMLSAXHandler extends DefaultHandler {
*/
private StringBuffer characters = new StringBuffer();


/** entity resolver */
EntityResolver entityResolver;

public void setEntityResolver(EntityResolver entityResolver) {
this.entityResolver = entityResolver;
}

public EntityResolver getEntityResolver() {
return entityResolver;
}

/**
* Delegate to {@link #entityResolver} if available.
* <p>
* <p>
* Note this is an XMLSchema based parser, all attempts to resolved DTDs are rejected.
* </p>
*
* TODO summary sentence for resolveEntity ...
*
* @see org.xml.sax.EntityResolver#resolveEntity(java.lang.String, java.lang.String)
* @param pubId
* @param sysId
* @return InputSource
* @throws SAXException
* @param publicId The public identifier, or null if none is available.
* @param systemId The system identifier provided in the XML document.
* @return The new input source, or null to require the default behavior.
* @exception java.io.IOException If there is an error setting up the new input source.
* @exception org.xml.sax.SAXException Any SAX exception, possibly wrapping another exception.
*/
public InputSource resolveEntity( String pubId, String sysId ) throws SAXException {
public InputSource resolveEntity( String publicId, String systemId ) throws SAXException, IOException {
// avoid dtd files
if(sysId != null && sysId.endsWith("dtd")){
return new InputSource(new StringReader(""));
}
try{
if (false) {
/*
* HACK: This dead code exists only in order to make J2SE 1.4 compiler happy.
* This hack is needed because there is a slight API change between J2SE 1.4
* and 1.5: the 'resolveEntity()' method didn't declared IOException in its
* throw clause in J2SE 1.4. Compare the two following links:
*
* http://java.sun.com/j2se/1.5/docs/api/org/xml/sax/helpers/DefaultHandler.html#resolveEntity(java.lang.String,%20java.lang.String)
* http://java.sun.com/j2se/1.4/docs/api/org/xml/sax/helpers/DefaultHandler.html#resolveEntity(java.lang.String,%20java.lang.String)
*/
throw new IOException();
}
return super.resolveEntity(pubId,sysId);
}catch(IOException e){
SAXException se = new SAXException(e.getLocalizedMessage());
se.initCause(e);
throw se;
}
if (systemId != null && systemId.endsWith("dtd")) {
return new InputSource(new StringReader(""));
}
if (entityResolver != null) {
return entityResolver.resolveEntity(publicId, systemId);
} else {
return super.resolveEntity(publicId, systemId);
}
}
// hints
private Map hints;
Expand Down Expand Up @@ -147,7 +153,7 @@ public InputSource resolveEntity( String pubId, String sysId ) throws SAXExcepti
*/
public XMLSAXHandler(URI intendedDocument, Map hints) {
instanceDocument = intendedDocument;
this.hints = hints;
init(hints);
logger.setLevel(level);
}

Expand All @@ -158,13 +164,21 @@ public XMLSAXHandler(URI intendedDocument, Map hints) {
* be provided, as this will allow the parser to resolve relative uri's.
* </p>
*
* @param hints DOCUMENT ME!
* @param hints Hints as per {@link {@link XMLHandlerHints}
*/
public XMLSAXHandler(Map hints) {
this.hints = hints;
init(hints);
logger.setLevel(level);
}

protected void init(Map hints){
if( hints == null ){
hints = Collections.EMPTY_MAP;
}
this.hints = hints;
if( hints.containsKey(XMLHandlerHints.ENTITY_RESOLVER)){
setEntityResolver( (EntityResolver) hints.get(XMLHandlerHints.ENTITY_RESOLVER));;
}
}
/**
* Implementation of endDocument.
*
Expand Down
Expand Up @@ -39,6 +39,7 @@
* @source $URL$
*/
public class DocumentHandler extends XMLElementHandler {
/** Supplied {@link Schema} for parsing and validation */
public final static String DEFAULT_NAMESPACE_HINT_KEY = "org.geotools.xml.handlers.DocumentHandler.DEFAULT_NAMESPACE_HINT_KEY";
private XMLElementHandler xeh = null;
private ElementHandlerFactory ehf;
Expand Down

0 comments on commit cda7263

Please sign in to comment.