mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
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:
@@ -35,7 +35,6 @@ import org.alfresco.service.cmr.repository.ContentWriter;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
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.TemplateService;
|
||||
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.LogFactory;
|
||||
|
||||
|
||||
/**
|
||||
* @author Brian
|
||||
*
|
||||
@@ -85,62 +83,6 @@ public class XSLTRenderingEngineTest extends BaseAlfrescoSpringTest
|
||||
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
|
||||
{
|
||||
try
|
||||
@@ -376,23 +318,6 @@ public class XSLTRenderingEngineTest extends BaseAlfrescoSpringTest
|
||||
"<xsl:template match=\"/\">" + "<xsl:for-each select=\"/nutrition/food\">"
|
||||
+ "<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\" "
|
||||
+ "xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" "
|
||||
+ "xmlns:fn=\"http://www.w3.org/2005/02/xpath-functions\"> " + "<xsl:output method=\"text\" />" +
|
||||
|
@@ -22,7 +22,6 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
@@ -49,7 +48,6 @@ import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.util.XMLUtil;
|
||||
import org.apache.bsf.BSFManager;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang.exception.ExceptionUtils;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.xml.utils.Constants;
|
||||
@@ -136,7 +134,7 @@ public class XSLTProcessor extends BaseProcessor implements TemplateProcessor
|
||||
Document xslTemplate;
|
||||
try
|
||||
{
|
||||
xslTemplate = XMLUtil.secureParseXSL(templateSource.getReader(defaultEncoding));
|
||||
xslTemplate = XMLUtil.parse(templateSource.getReader(defaultEncoding));
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
@@ -202,7 +200,7 @@ public class XSLTProcessor extends BaseProcessor implements TemplateProcessor
|
||||
throw new TransformerException("unable to resolve href " + href);
|
||||
}
|
||||
|
||||
Document d = XMLUtil.secureParseXSL(in);
|
||||
Document d = XMLUtil.parse(in);
|
||||
if (log.isDebugEnabled())
|
||||
{
|
||||
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");
|
||||
for (TransformerException te : errors)
|
||||
{
|
||||
msg.append("message: " + te.getMessageAndLocation()).append("\n");
|
||||
String cause = ExceptionUtils.getRootCauseMessage(te);
|
||||
if (cause != null)
|
||||
{
|
||||
msg.append(" caused by: " + cause);
|
||||
}
|
||||
msg.append(te.getMessageAndLocation()).append("\n");
|
||||
}
|
||||
throw new TemplateException(msg.toString());
|
||||
}
|
||||
@@ -290,11 +283,6 @@ public class XSLTProcessor extends BaseProcessor implements TemplateProcessor
|
||||
for (TransformerException te : errors)
|
||||
{
|
||||
msg.append(te.getMessageAndLocation()).append("\n");
|
||||
String cause = ExceptionUtils.getRootCauseMessage(te);
|
||||
if (cause != null)
|
||||
{
|
||||
msg.append(" caused by: " + cause);
|
||||
}
|
||||
}
|
||||
throw new TemplateException(msg.toString());
|
||||
}
|
||||
@@ -326,17 +314,18 @@ public class XSLTProcessor extends BaseProcessor implements TemplateProcessor
|
||||
final Element docEl = xslTemplate.getDocumentElement();
|
||||
final String XALAN_NS = Constants.S_BUILTIN_EXTENSIONS_URL;
|
||||
final String XALAN_NS_PREFIX = "xalan";
|
||||
docEl.setAttribute("xmlns:" + XALAN_NS_PREFIX, XALAN_NS);
|
||||
|
||||
final Set<String> excludePrefixes = new HashSet<String>();
|
||||
if (docEl.hasAttribute("exclude-result-prefixes"))
|
||||
{
|
||||
excludePrefixes.addAll(Arrays.asList(docEl.getAttribute("exclude-result-prefixes").split(" ")));
|
||||
}
|
||||
excludePrefixes.add(XALAN_NS_PREFIX);
|
||||
|
||||
final List<String> result = new LinkedList<String>();
|
||||
for (QName ns : methods.keySet())
|
||||
{
|
||||
|
||||
final String prefix = ns.getLocalName();
|
||||
docEl.setAttribute("xmlns:" + prefix, ns.getNamespaceURI());
|
||||
excludePrefixes.add(prefix);
|
||||
@@ -376,11 +365,6 @@ public class XSLTProcessor extends BaseProcessor implements TemplateProcessor
|
||||
}
|
||||
docEl.setAttribute("exclude-result-prefixes", StringUtils.join(excludePrefixes
|
||||
.toArray(new String[excludePrefixes.size()]), " "));
|
||||
if (log.isDebugEnabled()) {
|
||||
StringWriter writer = new StringWriter();
|
||||
XMLUtil.print(xslTemplate, writer);
|
||||
log.debug(writer);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@@ -30,7 +30,6 @@ import org.alfresco.service.cmr.repository.ContentWriter;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
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.TemplateService;
|
||||
import org.alfresco.service.cmr.search.ResultSet;
|
||||
@@ -77,60 +76,6 @@ public class XSLTProcessorTest extends BaseAlfrescoSpringTest
|
||||
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
|
||||
{
|
||||
try
|
||||
@@ -320,23 +265,6 @@ public class XSLTProcessorTest extends BaseAlfrescoSpringTest
|
||||
|
||||
"<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 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>";
|
||||
}
|
||||
|
@@ -19,17 +19,7 @@
|
||||
|
||||
package org.alfresco.util;
|
||||
|
||||
import java.io.CharArrayReader;
|
||||
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 java.io.*;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
@@ -38,33 +28,18 @@ import javax.xml.transform.OutputKeys;
|
||||
import javax.xml.transform.Transformer;
|
||||
import javax.xml.transform.TransformerException;
|
||||
import javax.xml.transform.TransformerFactory;
|
||||
import javax.xml.transform.dom.DOMResult;
|
||||
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 org.alfresco.model.ContentModel;
|
||||
import org.alfresco.service.cmr.avm.AVMService;
|
||||
import org.alfresco.service.cmr.repository.ContentReader;
|
||||
import org.alfresco.service.cmr.repository.ContentService;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.w3c.dom.Attr;
|
||||
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.w3c.dom.*;
|
||||
import org.xml.sax.InputSource;
|
||||
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.
|
||||
@@ -139,213 +114,68 @@ public class XMLUtil
|
||||
}
|
||||
|
||||
/** utility function for parsing xml */
|
||||
public static Document parse(final String source, final XMLFilter... filters)
|
||||
public static Document parse(final String source)
|
||||
throws SAXException,
|
||||
IOException
|
||||
{
|
||||
return XMLUtil.parse(new CharArrayReader(source.toCharArray()), filters);
|
||||
}
|
||||
|
||||
public static Document secureParseXSL (final String source, final XMLFilter... filters)
|
||||
throws SAXException,
|
||||
IOException
|
||||
{
|
||||
return parse(new CharArrayReader(source.toCharArray()), addSecurityFilter(filters));
|
||||
return XMLUtil.parse(new ByteArrayInputStream(source.getBytes("UTF-8")));
|
||||
}
|
||||
|
||||
/** utility function for parsing xml */
|
||||
public static Document parse(final NodeRef nodeRef,
|
||||
final ContentService contentService,
|
||||
final XMLFilter... filters)
|
||||
final ContentService contentService)
|
||||
throws SAXException,
|
||||
IOException
|
||||
{
|
||||
final ContentReader contentReader =
|
||||
contentService.getReader(nodeRef, ContentModel.TYPE_CONTENT);
|
||||
final InputStream in = contentReader.getContentInputStream();
|
||||
return XMLUtil.parse(in, filters);
|
||||
}
|
||||
|
||||
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));
|
||||
return XMLUtil.parse(in);
|
||||
}
|
||||
|
||||
/** utility function for parsing xml */
|
||||
public static Document parse(final int version,
|
||||
final String path,
|
||||
final AVMService avmService,
|
||||
final XMLFilter...filters)
|
||||
final AVMService avmService)
|
||||
throws SAXException,
|
||||
IOException
|
||||
{
|
||||
return XMLUtil.parse(avmService.getFileInputStream(version, path), filters);
|
||||
}
|
||||
|
||||
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));
|
||||
return XMLUtil.parse(avmService.getFileInputStream(version, path));
|
||||
}
|
||||
|
||||
/** utility function for parsing xml */
|
||||
public static Document parse(final File source,
|
||||
final XMLFilter... filters)
|
||||
public static Document parse(final File source)
|
||||
throws SAXException,
|
||||
IOException
|
||||
{
|
||||
return XMLUtil.parse(new FileInputStream(source), filters);
|
||||
return XMLUtil.parse(new FileInputStream(source));
|
||||
}
|
||||
|
||||
public static Document secureParseXSL(final File source,
|
||||
final XMLFilter... filters)
|
||||
/** utility function for parsing xml */
|
||||
public static Document parse(final InputStream source)
|
||||
throws SAXException,
|
||||
IOException
|
||||
{
|
||||
return parse(new FileInputStream(source), addSecurityFilter(filters));
|
||||
}
|
||||
|
||||
private static Document parseWithXMLFilters(final InputSource source,
|
||||
final XMLFilter... filters)
|
||||
throws SAXException,
|
||||
IOException
|
||||
try
|
||||
{
|
||||
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.
|
||||
final DocumentBuilder db = XMLUtil.getDocumentBuilder();
|
||||
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 */
|
||||
public static Document parse(final InputStream source, final XMLFilter... filters)
|
||||
throws SAXException,
|
||||
IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
return parseWithXMLFilters(new InputSource(source), filters);
|
||||
}
|
||||
finally
|
||||
{
|
||||
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 */
|
||||
public static Document parse(final Reader source,
|
||||
final XMLFilter... filters)
|
||||
public static Document parse(final Reader source)
|
||||
throws SAXException,
|
||||
IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
return parseWithXMLFilters(new InputSource(source), filters);
|
||||
final DocumentBuilder db = XMLUtil.getDocumentBuilder();
|
||||
return db.parse(new InputSource(source));
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -353,15 +183,6 @@ public class XMLUtil
|
||||
}
|
||||
}
|
||||
|
||||
/** 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 */
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user