mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-31 17:39:05 +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:
@@ -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,229 +114,75 @@ 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);
|
||||
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 */
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
return XMLUtil.parse(new FileInputStream(source));
|
||||
}
|
||||
|
||||
/** utility function for parsing xml */
|
||||
public static Document parse(final InputStream source, final XMLFilter... filters)
|
||||
public static Document parse(final InputStream source)
|
||||
throws SAXException,
|
||||
IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
return parseWithXMLFilters(new InputSource(source), filters);
|
||||
final DocumentBuilder db = XMLUtil.getDocumentBuilder();
|
||||
return db.parse(source);
|
||||
}
|
||||
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
|
||||
{
|
||||
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 */
|
||||
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