Merged V4.1-BUG-FIX to HEAD

39828: Merged V4.1 to V4.1-BUG-FIX
      39827: Merged PATCHES/V4.0.2 to V4.1
         39825: ALF-13453 / ALF-13844: Merged V3.4-BUG-FIX to PATCHES/V4.0.2
            39823: ALF-13552, ALF-13978: Reverse merged the following revisions - won't fix due to regressions and not a serious vulnerability
               35341: ALF-13552: Merged V4.0 to V3.4
                  35296: ALF-13453: Merged V4.0-BUG-FIX to V4.0
                     35295: Fix for ALF-13453: Remote Code Execution (can create reverse shell)
                  35304: ALF-13453: Extra fix to ensure xalan namespace isn't declared with global scope and can't be hijacked by an input stylesheet
                  35307: ALF-13453: Duplicated extra fix to duplicate code in XSLTRenderingEngine!
               36101: ALF-13978: Merged V4.0-BUG-FIX to V3.4
                  36014: ALF-13844: XSLT Filtering Not 100% Secure
                     - added more namespaces to the security filter.
                     - verified that include/import uses the security filter.
               36108: ALF-13978: Fixed compilation errors
            39824: ALF-13552, ALF-13978: Fixed compilation errors


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@39829 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Dave Ward
2012-07-26 15:50:56 +00:00
parent 05bcd40a17
commit faa9ba7118
4 changed files with 22 additions and 441 deletions

View File

@@ -35,7 +35,6 @@ import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.repository.TemplateException;
import org.alfresco.service.cmr.repository.TemplateProcessor; import org.alfresco.service.cmr.repository.TemplateProcessor;
import org.alfresco.service.cmr.repository.TemplateService; import org.alfresco.service.cmr.repository.TemplateService;
import org.alfresco.service.cmr.search.ResultSet; import org.alfresco.service.cmr.search.ResultSet;
@@ -46,7 +45,6 @@ import org.alfresco.util.GUID;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
/** /**
* @author Brian * @author Brian
* *
@@ -84,62 +82,6 @@ public class XSLTRenderingEngineTest extends BaseAlfrescoSpringTest
"/app:company_home"); "/app:company_home");
this.companyHome = rs.getNodeRef(0); this.companyHome = rs.getNodeRef(0);
} }
public void testSecurityFilter() throws Exception
{
try
{
FileInfo file = createXmlFile(companyHome);
FileInfo xslFile = createXmlFile(companyHome, insecureVerySimpleXSLT);
RenditionDefinition def = renditionService.createRenditionDefinition(QName.createQName("Test"), XSLTRenderingEngine.NAME);
def.setParameterValue(XSLTRenderingEngine.PARAM_TEMPLATE_NODE, xslFile.getNodeRef());
ChildAssociationRef rendition = renditionService.render(file.getNodeRef(), def);
log.error("This insecure template should not process!");
fail();
}
catch (TemplateException e)
{
//pass!
}
catch (Exception ex)
{
log.error("Error!", ex);
fail();
}
}
public void testIncludeSecurityFilter() throws Exception
{
try
{
FileInfo file = createXmlFile(companyHome);
FileInfo insecureXSLFile = createXmlFile(companyHome, insecureVerySimpleXSLT);
String includeInsecureXSLFile = String.format(insecureIncludeVerySimpleXSLT, insecureXSLFile.getName());
FileInfo xslFile = createXmlFile(companyHome, includeInsecureXSLFile);
RenditionDefinition def = renditionService.createRenditionDefinition(QName.createQName("Test"), XSLTRenderingEngine.NAME);
def.setParameterValue(XSLTRenderingEngine.PARAM_TEMPLATE_NODE, xslFile.getNodeRef());
ChildAssociationRef rendition = renditionService.render(file.getNodeRef(), def);
log.error("This insecure include template should not process!");
fail();
}
catch (TemplateException e)
{
//pass!
}
catch (Exception ex)
{
log.error("Error!", ex);
fail();
}
}
public void testSimplestStringTemplate() throws Exception public void testSimplestStringTemplate() throws Exception
{ {
@@ -376,23 +318,6 @@ public class XSLTRenderingEngineTest extends BaseAlfrescoSpringTest
"<xsl:template match=\"/\">" + "<xsl:for-each select=\"/nutrition/food\">" "<xsl:template match=\"/\">" + "<xsl:for-each select=\"/nutrition/food\">"
+ "<xsl:value-of select=\"name\"/>" + "</xsl:for-each>" + "</xsl:template>" + "</xsl:stylesheet>"; + "<xsl:value-of select=\"name\"/>" + "</xsl:for-each>" + "</xsl:template>" + "</xsl:stylesheet>";
private String insecureVerySimpleXSLT = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<xsl:stylesheet version=\"1.0\" "
+ "xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" "
+ "xmlns:rt=\"http://xml.apache.org/xalan/java/java.lang.Runtime\"> "
+ "xmlns:fn=\"http://www.w3.org/2005/02/xpath-functions\"> " + "<xsl:output method=\"text\" />" +
"<xsl:preserve-space elements=\"*\"/>" +
"<xsl:template match=\"/\">" + "<xsl:for-each select=\"/nutrition/food\">"
+ "<xsl:value-of select=\"name\"/>" + "</xsl:for-each>" + "</xsl:template>" + "</xsl:stylesheet>";
private String insecureIncludeVerySimpleXSLT = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<xsl:stylesheet version=\"1.0\" "
+ "xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" >"
+ "<xsl:template match=\"/\">"
+ "</xsl:template>"
+ "<xsl:include href=\"%1$s\"/>"
+ "</xsl:stylesheet>";
private String callParseXmlDocument = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<xsl:stylesheet version=\"1.0\" " private String callParseXmlDocument = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<xsl:stylesheet version=\"1.0\" "
+ "xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" " + "xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" "
+ "xmlns:fn=\"http://www.w3.org/2005/02/xpath-functions\"> " + "<xsl:output method=\"text\" />" + + "xmlns:fn=\"http://www.w3.org/2005/02/xpath-functions\"> " + "<xsl:output method=\"text\" />" +

View File

@@ -22,7 +22,6 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.Reader; import java.io.Reader;
import java.io.StringReader; import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer; import java.io.Writer;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
@@ -49,7 +48,6 @@ import org.alfresco.service.namespace.QName;
import org.alfresco.util.XMLUtil; import org.alfresco.util.XMLUtil;
import org.apache.bsf.BSFManager; import org.apache.bsf.BSFManager;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.apache.xml.utils.Constants; import org.apache.xml.utils.Constants;
@@ -136,7 +134,7 @@ public class XSLTProcessor extends BaseProcessor implements TemplateProcessor
Document xslTemplate; Document xslTemplate;
try try
{ {
xslTemplate = XMLUtil.secureParseXSL(templateSource.getReader(defaultEncoding)); xslTemplate = XMLUtil.parse(templateSource.getReader(defaultEncoding));
} }
catch (IOException ex) catch (IOException ex)
{ {
@@ -202,7 +200,7 @@ public class XSLTProcessor extends BaseProcessor implements TemplateProcessor
throw new TransformerException("unable to resolve href " + href); throw new TransformerException("unable to resolve href " + href);
} }
Document d = XMLUtil.secureParseXSL(in); Document d = XMLUtil.parse(in);
if (log.isDebugEnabled()) if (log.isDebugEnabled())
{ {
log.debug("loaded " + XMLUtil.toString(d)); log.debug("loaded " + XMLUtil.toString(d));
@@ -241,12 +239,7 @@ public class XSLTProcessor extends BaseProcessor implements TemplateProcessor
final StringBuilder msg = new StringBuilder("errors encountered creating tranformer ... \n"); final StringBuilder msg = new StringBuilder("errors encountered creating tranformer ... \n");
for (TransformerException te : errors) for (TransformerException te : errors)
{ {
msg.append("message: " + te.getMessageAndLocation()).append("\n"); msg.append(te.getMessageAndLocation()).append("\n");
String cause = ExceptionUtils.getRootCauseMessage(te);
if (cause != null)
{
msg.append(" caused by: " + cause);
}
} }
throw new TemplateException(msg.toString()); throw new TemplateException(msg.toString());
} }
@@ -290,11 +283,6 @@ public class XSLTProcessor extends BaseProcessor implements TemplateProcessor
for (TransformerException te : errors) for (TransformerException te : errors)
{ {
msg.append(te.getMessageAndLocation()).append("\n"); msg.append(te.getMessageAndLocation()).append("\n");
String cause = ExceptionUtils.getRootCauseMessage(te);
if (cause != null)
{
msg.append(" caused by: " + cause);
}
} }
throw new TemplateException(msg.toString()); throw new TemplateException(msg.toString());
} }
@@ -326,17 +314,18 @@ public class XSLTProcessor extends BaseProcessor implements TemplateProcessor
final Element docEl = xslTemplate.getDocumentElement(); final Element docEl = xslTemplate.getDocumentElement();
final String XALAN_NS = Constants.S_BUILTIN_EXTENSIONS_URL; final String XALAN_NS = Constants.S_BUILTIN_EXTENSIONS_URL;
final String XALAN_NS_PREFIX = "xalan"; final String XALAN_NS_PREFIX = "xalan";
docEl.setAttribute("xmlns:" + XALAN_NS_PREFIX, XALAN_NS);
final Set<String> excludePrefixes = new HashSet<String>(); final Set<String> excludePrefixes = new HashSet<String>();
if (docEl.hasAttribute("exclude-result-prefixes")) if (docEl.hasAttribute("exclude-result-prefixes"))
{ {
excludePrefixes.addAll(Arrays.asList(docEl.getAttribute("exclude-result-prefixes").split(" "))); excludePrefixes.addAll(Arrays.asList(docEl.getAttribute("exclude-result-prefixes").split(" ")));
} }
excludePrefixes.add(XALAN_NS_PREFIX);
final List<String> result = new LinkedList<String>(); final List<String> result = new LinkedList<String>();
for (QName ns : methods.keySet()) for (QName ns : methods.keySet())
{ {
final String prefix = ns.getLocalName(); final String prefix = ns.getLocalName();
docEl.setAttribute("xmlns:" + prefix, ns.getNamespaceURI()); docEl.setAttribute("xmlns:" + prefix, ns.getNamespaceURI());
excludePrefixes.add(prefix); excludePrefixes.add(prefix);
@@ -376,11 +365,6 @@ public class XSLTProcessor extends BaseProcessor implements TemplateProcessor
} }
docEl.setAttribute("exclude-result-prefixes", StringUtils.join(excludePrefixes docEl.setAttribute("exclude-result-prefixes", StringUtils.join(excludePrefixes
.toArray(new String[excludePrefixes.size()]), " ")); .toArray(new String[excludePrefixes.size()]), " "));
if (log.isDebugEnabled()) {
StringWriter writer = new StringWriter();
XMLUtil.print(xslTemplate, writer);
log.debug(writer);
}
return result; return result;
} }

View File

@@ -30,7 +30,6 @@ import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.repository.TemplateException;
import org.alfresco.service.cmr.repository.TemplateProcessor; import org.alfresco.service.cmr.repository.TemplateProcessor;
import org.alfresco.service.cmr.repository.TemplateService; import org.alfresco.service.cmr.repository.TemplateService;
import org.alfresco.service.cmr.search.ResultSet; import org.alfresco.service.cmr.search.ResultSet;
@@ -76,60 +75,6 @@ public class XSLTProcessorTest extends BaseAlfrescoSpringTest
"/app:company_home"); "/app:company_home");
this.companyHome = rs.getNodeRef(0); this.companyHome = rs.getNodeRef(0);
} }
public void testSecurityFilter() throws Exception
{
try
{
FileInfo file = createXmlFile(companyHome);
XSLTemplateModel model = new XSLTemplateModel();
model.put(XSLTProcessor.ROOT_NAMESPACE, XMLUtil.parse(file.getNodeRef(), contentService));
StringWriter writer = new StringWriter();
xsltProcessor.processString(insecureVerySimpleXSLT, model, writer);
log.error("This insecure template should not process!");
fail();
}
catch (TemplateException e)
{
//pass!
}
catch (Exception ex)
{
log.error("Error!", ex);
fail();
}
}
public void testIncludeSecurityFilter() throws Exception
{
try
{
FileInfo file = createXmlFile(companyHome);
FileInfo insecureXSLFile = createXmlFile(companyHome, insecureVerySimpleXSLT);
String includeInsecureXSLTemplate = String.format(insecureIncludeVerySimpleXSLT, insecureXSLFile.getName());
FileInfo includeInsecureXSLFile = createXmlFile(companyHome, includeInsecureXSLTemplate);
XSLTemplateModel model = new XSLTemplateModel();
model.put(XSLTProcessor.ROOT_NAMESPACE, XMLUtil.parse(file.getNodeRef(), contentService));
StringWriter writer = new StringWriter();
xsltProcessor.process(includeInsecureXSLFile.getNodeRef().toString(), model, writer);
log.error("This insecure include template should not process!");
fail();
}
catch (TemplateException e)
{
//pass!
}
catch (Exception ex)
{
log.error("Error!", ex);
fail();
}
}
public void testSimplestStringTemplate() throws Exception public void testSimplestStringTemplate() throws Exception
{ {
@@ -322,21 +267,4 @@ public class XSLTProcessorTest extends BaseAlfrescoSpringTest
"<xsl:template match=\"/\">" + "<xsl:for-each select=\"/nutrition/food\">" "<xsl:template match=\"/\">" + "<xsl:for-each select=\"/nutrition/food\">"
+ "<xsl:value-of select=\"name\"/>" + "</xsl:for-each>" + "</xsl:template>" + "</xsl:stylesheet>"; + "<xsl:value-of select=\"name\"/>" + "</xsl:for-each>" + "</xsl:template>" + "</xsl:stylesheet>";
private String insecureIncludeVerySimpleXSLT = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<xsl:stylesheet version=\"1.0\" "
+ "xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" >"
+ "<xsl:template match=\"/\">"
+ "</xsl:template>"
+ "<xsl:include href=\"%1$s\"/>"
+ "</xsl:stylesheet>";
private String insecureVerySimpleXSLT = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<xsl:stylesheet version=\"1.0\" "
+ "xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" "
+ "xmlns:rt=\"http://xml.apache.org/xalan/java/java.lang.Runtime\"> "
+ "xmlns:fn=\"http://www.w3.org/2005/02/xpath-functions\"> " + "<xsl:output method=\"text\" />" +
"<xsl:preserve-space elements=\"*\"/>" +
"<xsl:template match=\"/\">" + "<xsl:for-each select=\"/nutrition/food\">"
+ "<xsl:value-of select=\"name\"/>" + "</xsl:for-each>" + "</xsl:template>" + "</xsl:stylesheet>";
} }

View File

@@ -19,17 +19,7 @@
package org.alfresco.util; package org.alfresco.util;
import java.io.CharArrayReader; import java.io.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.LinkedList;
import java.util.List;
import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.DocumentBuilderFactory;
@@ -38,33 +28,18 @@ import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer; import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory; import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource; import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TransformerHandler;
import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamResult;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.avm.AVMService; import org.alfresco.service.cmr.avm.AVMService;
import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Attr; import org.w3c.dom.*;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource; import org.xml.sax.InputSource;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import org.xml.sax.XMLFilter;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLFilterImpl;
import org.xml.sax.helpers.XMLReaderFactory;
/** /**
* XML utility functions. * XML utility functions.
@@ -139,229 +114,75 @@ public class XMLUtil
} }
/** utility function for parsing xml */ /** utility function for parsing xml */
public static Document parse(final String source, final XMLFilter... filters) public static Document parse(final String source)
throws SAXException, throws SAXException,
IOException IOException
{ {
return XMLUtil.parse(new CharArrayReader(source.toCharArray()), filters); return XMLUtil.parse(new ByteArrayInputStream(source.getBytes("UTF-8")));
}
public static Document secureParseXSL (final String source, final XMLFilter... filters)
throws SAXException,
IOException
{
return parse(new CharArrayReader(source.toCharArray()), addSecurityFilter(filters));
} }
/** utility function for parsing xml */ /** utility function for parsing xml */
public static Document parse(final NodeRef nodeRef, public static Document parse(final NodeRef nodeRef,
final ContentService contentService, final ContentService contentService)
final XMLFilter... filters)
throws SAXException, throws SAXException,
IOException IOException
{ {
final ContentReader contentReader = final ContentReader contentReader =
contentService.getReader(nodeRef, ContentModel.TYPE_CONTENT); contentService.getReader(nodeRef, ContentModel.TYPE_CONTENT);
final InputStream in = contentReader.getContentInputStream(); final InputStream in = contentReader.getContentInputStream();
return XMLUtil.parse(in, filters); return XMLUtil.parse(in);
} }
public static Document secureParseXSL(final NodeRef nodeRef,
final ContentService contentService,
final XMLFilter... filters)
throws SAXException,
IOException
{
final ContentReader contentReader =
contentService.getReader(nodeRef, ContentModel.TYPE_CONTENT);
final InputStream in = contentReader.getContentInputStream();
return parse(in, addSecurityFilter(filters));
}
/** utility function for parsing xml */ /** utility function for parsing xml */
public static Document parse(final int version, public static Document parse(final int version,
final String path, final String path,
final AVMService avmService, final AVMService avmService)
final XMLFilter...filters)
throws SAXException, throws SAXException,
IOException IOException
{ {
return XMLUtil.parse(avmService.getFileInputStream(version, path), filters); return XMLUtil.parse(avmService.getFileInputStream(version, path));
}
public static Document secureParseXSL(final int version,
final String path,
final AVMService avmService,
final XMLFilter... filters)
throws SAXException,
IOException
{
return parse(avmService.getFileInputStream(version, path), addSecurityFilter(filters));
} }
/** utility function for parsing xml */ /** utility function for parsing xml */
public static Document parse(final File source, public static Document parse(final File source)
final XMLFilter... filters)
throws SAXException, throws SAXException,
IOException IOException
{ {
return XMLUtil.parse(new FileInputStream(source), filters); return XMLUtil.parse(new FileInputStream(source));
}
public static Document secureParseXSL(final File source,
final XMLFilter... filters)
throws SAXException,
IOException
{
return parse(new FileInputStream(source), addSecurityFilter(filters));
}
private static Document parseWithXMLFilters(final InputSource source,
final XMLFilter... filters)
throws SAXException,
IOException
{
return parseWithXMLFilters(source, false, filters);
}
private static Document parseWithXMLFilters(final InputSource source,
final boolean validating,
final XMLFilter... filters)
throws SAXException,
IOException
{
TransformerFactory tf = TransformerFactory.newInstance();
// Check to make sure this is a SAX TransformerFactory
if (!tf.getFeature(SAXTransformerFactory.FEATURE))
{
throw new SAXException("SAX Transformation factory not found.");
}
// Cast to appropriate factory class
SAXTransformerFactory stf = (SAXTransformerFactory) tf;
final DocumentBuilder db = XMLUtil.getDocumentBuilder(true, validating);
if (filters == null || filters.length == 0)
{
// No filters. Process this as normal.
return db.parse(source);
}
else
{
// Process with filters
try
{
final Document doc = db.newDocument();
final TransformerHandler th = stf.newTransformerHandler();
// Specify transformation to DOMResult with empty Node container (Document)
th.setResult(new DOMResult(doc));
XMLReader reader = XMLReaderFactory.createXMLReader();
//emulate what the document builder parser supports
//all readers are required to support namespaces and namespace-prefixes
reader.setFeature("http://xml.org/sax/features/namespaces", db.isNamespaceAware());
reader.setFeature("http://xml.org/sax/features/namespace-prefixes", db.isNamespaceAware() ? true : false);
// Chain multiple filters together
int i = 0;
XMLFilter filter = null;
for (XMLFilter f : filters)
{
// there can be no null in the filter list
if (f == null)
throw new SAXException("Nulls are not allowed in XML filter list.");
// if first item then set new reader
if (i == 0)
f.setParent(reader);
else
// set parent filter to previous element in the array
f.setParent(filters[i - 1]);
filter = f;
i++;
}
//not sure how filter could be null
if (filter != null)
{
filter.setContentHandler(th);
filter.parse(source);
try
{
//try to activate/deactivate validation
filter.setFeature("http://xml.org/sax/features/validation", db.isValidating());
}
catch (SAXException se)
{
LOGGER.warn("XML reader does not support validation feature.", se);
}
}
else
{
//not sure how we could get here
throw new SAXException("No XML filters available to process this request.");
}
if (LOGGER.isDebugEnabled()) {
StringWriter writer = new StringWriter();
XMLUtil.print(doc, writer);
LOGGER.debug(writer);
}
return doc;
}
catch (TransformerException tce)
{
throw new SAXException(tce);
}
}
} }
/** utility function for parsing xml */ /** utility function for parsing xml */
public static Document parse(final InputStream source, final XMLFilter... filters) public static Document parse(final InputStream source)
throws SAXException, throws SAXException,
IOException IOException
{ {
try try
{ {
return parseWithXMLFilters(new InputSource(source), filters); final DocumentBuilder db = XMLUtil.getDocumentBuilder();
return db.parse(source);
} }
finally finally
{ {
source.close(); source.close();
} }
} }
/** secure parse for InputStream source */
public static Document secureParseXSL(final InputStream source,
final XMLFilter... filters)
throws SAXException,
IOException
{
return parse(source, addSecurityFilter(filters));
}
/** utility function for parsing xml */ /** utility function for parsing xml */
public static Document parse(final Reader source, public static Document parse(final Reader source)
final XMLFilter... filters)
throws SAXException, throws SAXException,
IOException IOException
{ {
try try
{ {
return parseWithXMLFilters(new InputSource(source), filters); final DocumentBuilder db = XMLUtil.getDocumentBuilder();
return db.parse(new InputSource(source));
} }
finally finally
{ {
source.close(); source.close();
} }
} }
/** secure parse for Reader source **/
public static Document secureParseXSL(final Reader source,
final XMLFilter... filters)
throws SAXException,
IOException
{
return parse(source, addSecurityFilter(filters));
}
/** provides a document builder that is namespace aware but not validating by default */ /** provides a document builder that is namespace aware but not validating by default */
public static DocumentBuilder getDocumentBuilder() public static DocumentBuilder getDocumentBuilder()
{ {
@@ -472,81 +293,4 @@ public class XMLUtil
} }
}; };
} }
/**
* returns a new array of filters with the security filter at the head of the array
*/
private static XMLFilter[] addSecurityFilter(XMLFilter...filters) {
if (filters == null || filters.length == 0) {
return new XMLFilter[] {new FastFailSecureXMLFilter()};
} else {
XMLFilter[] xmlfilters = new XMLFilter[filters.length + 1];
xmlfilters[0] = new FastFailSecureXMLFilter();
System.arraycopy(filters, 0, xmlfilters, 1, filters.length);
return xmlfilters;
}
}
/**
* XMLFilter that throws an exception when it comes across any insecure namespaces
*/
private static class FastFailSecureXMLFilter extends XMLFilterImpl
{
private static final List<String> insecureURIs = new LinkedList<String>()
{
private static final long serialVersionUID = 1L;
{
add("xalan://");
add("http://exslt.org/");
add("http://xml.apache.org/xalan/PipeDocument");
add("http://xml.apache.org/xalan/sql");
add("http://xml.apache.org/xalan/redirect");
add("http://xml.apache.org/xalan/xsltc/java");
add("http://xml.apache.org/xalan/java");
add("http://xml.apache.org/xslt");
add("http://xml.apache.org/java");
}
};
public FastFailSecureXMLFilter()
{
};
public void startPrefixMapping(String prefix, String uri)
throws SAXException
{
if (isInsecureURI(uri))
{
throw new SAXException("Insecure namespace: " + uri);
}
super.startPrefixMapping(prefix, uri);
}
public void startElement (String uri,
String localName,
String qName,
final Attributes atts)
throws SAXException
{
if (isInsecureURI(uri))
{
throw new SAXException("Insecure namespace: " + uri);
}
super.startElement(uri, localName, qName, atts);
}
private boolean isInsecureURI(String uri)
{
for (String insecureURI : insecureURIs)
{
if (StringUtils.startsWithIgnoreCase(uri, insecureURI)) return true;
}
return false;
}
}
} }