Merged HEAD-BUG-FIX (5.1/Cloud) to HEAD (5.1/Cloud)

98426: Merged 5.0.N (5.0.2) to HEAD-BUG-FIX (5.1/Cloud)
      98384: Merged 5.0.1 (5.0.1) to 5.0.N (5.0.2)
         98286: MNT-13473: Merged CLOUD39 (Cloud 39.3) to 5.0.1 (5.0.1)
            97951: MNT-13456:


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@98533 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Alan Davis
2015-03-03 23:43:09 +00:00
parent d0f7fdedf7
commit 0dd0c87f93
4 changed files with 184 additions and 22 deletions

View File

@@ -1427,6 +1427,15 @@
<property name="linksService" ref="LinksService"/>
<property name="personService" ref="PersonService"/>
<property name="activityService" ref="activityService"/>
<property name="protocolsWhiteList"><value>${links.protocosl.white.list}</value></property>
<property name="xssRegexp">
<list>
<value>.*s((\\*)|(\\[0]*)*)c((\\*)|(\\[0]*)*)r((\\*)|(\\[0]*)*)i((\\*)|(\\[0]*)*)p((\\*)|(\\[0]*)*)t((\\*)|(\\[0]*)*):.*</value>
<value>.*on.*\(.*\).*=.*</value>
<value>&lt;#{'$'}</value>
<value>^&lt;.*</value>
</list>
</property>
</bean>
<!-- Fetches the details of one link -->

View File

@@ -24,6 +24,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import org.alfresco.query.PagingRequest;
import org.alfresco.repo.content.MimetypeMap;
@@ -70,6 +71,10 @@ public abstract class AbstractLinksWebScript extends DeclarativeWebScript
protected PersonService personService;
protected ActivityService activityService;
private String protocolsWhiteList = "http,https,ftp,mailto";
private ArrayList<String> allowedProtocols;
private ArrayList<Pattern> xssPatterns;
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
@@ -95,6 +100,98 @@ public abstract class AbstractLinksWebScript extends DeclarativeWebScript
this.activityService = activityService;
}
public void setProtocolsWhiteList(String protocolsWhiteList)
{
this.protocolsWhiteList = protocolsWhiteList;
}
public void setXssRegexp(ArrayList<String> xssRegexp)
{
xssPatterns = new ArrayList<>(xssRegexp.size());
for (String xssRegexpStr : xssRegexp)
{
xssPatterns.add(Pattern.compile(xssRegexpStr));
}
}
private boolean isProtocolAllowed(String protocol)
{
// will be used default protocol prefix
if (protocol.length() == 0)
{
return true;
}
if (allowedProtocols == null)
{
allowedProtocols = new ArrayList<String>();
for (String delimProtocol : protocolsWhiteList.split(","))
{
if (delimProtocol.trim().length() == 0)
{
continue;
}
allowedProtocols.add(delimProtocol.trim());
}
}
return allowedProtocols.contains(protocol);
}
private boolean isPossibleXSS(String url)
{
// check for null
if (xssPatterns == null)
{
return false;
}
boolean result = false;
for (Pattern pattern : xssPatterns)
{
if (pattern.matcher(url).matches())
{
result = true;
}
}
return result;
}
private boolean isUrlCorrect(String url)
{
//default behavior if url absent
if (url == null)
{
return true;
}
if (url.trim().length() == 0 || isPossibleXSS(url))
{
return false;
}
int colonPos = url.indexOf(":");
colonPos = colonPos > 0 ? colonPos : 0;
String protocol = url.substring(0, colonPos);
boolean result = isProtocolAllowed(protocol);
//check for record host:port e.g.: localhost:8080
if (!result)
{
String secondUrlPart = url.substring(colonPos+1);
int slashPos = secondUrlPart.indexOf("/");
slashPos = slashPos > 0 ? slashPos : secondUrlPart.length();
String port = secondUrlPart.substring(0, slashPos);
Pattern p = Pattern.compile("^[0-9]*$");
if (p.matcher(port).matches())
{
result = true;
}
}
return result;
}
protected String getOrNull(JSONObject json, String key)
{
@@ -307,6 +404,17 @@ public abstract class AbstractLinksWebScript extends DeclarativeWebScript
// Link name is optional
String linkName = templateVars.get("path");
//sanitise url
if (json != null)
{
String url = getOrNull(json, "url");
if (!isUrlCorrect(url))
{
String error = "Url not allowed";
throw new WebScriptException(Status.STATUS_BAD_REQUEST, error);
}
}
// Have the real work done
return executeImpl(site, linkName, req, json, status, cache);
}

View File

@@ -18,10 +18,12 @@
*/
package org.alfresco.repo.web.scripts.links;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.regex.Pattern;
import org.alfresco.repo.security.permissions.AccessDeniedException;
import org.alfresco.service.cmr.links.LinkInfo;
@@ -61,12 +63,13 @@ public class LinkPut extends AbstractLinksWebScript
return model;
}
// Get the new link details from the JSON
// Update the main properties
link.setTitle(getOrNull(json, "title"));
link.setDescription(getOrNull(json, "description"));
link.setURL(getOrNull(json, "url"));
String url = getOrNull(json, "url");
link.setURL(url);
// Handle internal / not internal
if (json.containsKey("internal"))

View File

@@ -20,6 +20,7 @@ package org.alfresco.repo.web.scripts.links;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import javax.transaction.UserTransaction;
@@ -539,6 +540,47 @@ public class LinksRestApiTest extends BaseWebScriptTest
deleteLink(name, Status.STATUS_NOT_FOUND);
}
/**
* MNT-13456 Check for XSS attack via update of link
* @throws Exception
*/
public void testXssLinks() throws Exception
{
String LINK_TITLE = "lnk" + System.currentTimeMillis();
String LINK_URL = "http://alfresco.com";
HashMap<String, Integer> mapForCheck = new HashMap<String, Integer>();
mapForCheck.put("http:javasc\\ript:alert('mail.ru')", Status.STATUS_BAD_REQUEST);
mapForCheck.put("javas\\0cr\\ip\\00t:alert('dd')", Status.STATUS_BAD_REQUEST);
mapForCheck.put("alfresco.my", Status.STATUS_OK);
mapForCheck.put("javascript:alert('http://somedata.html')", Status.STATUS_BAD_REQUEST);
mapForCheck.put("http://alfresco.org", Status.STATUS_OK);
mapForCheck.put("localhost:8080", Status.STATUS_OK);
mapForCheck.put("localhost:8080/share", Status.STATUS_OK);
mapForCheck.put("localhost:80A80/share", Status.STATUS_BAD_REQUEST);
mapForCheck.put("http:java\\00script:alert('XSS')", Status.STATUS_BAD_REQUEST);
mapForCheck.put("http:javas\\0cript:alert('XSS')", Status.STATUS_BAD_REQUEST);
mapForCheck.put("http: &#14; javascript:alert('XSS')", Status.STATUS_BAD_REQUEST);
mapForCheck.put("<SCRIPT/XSS SRC='http://ha.ckers.org/xss.js'></SCRIPT>", Status.STATUS_BAD_REQUEST);
mapForCheck.put("<iframe src=http://ha.ckers.org/scriptlet.html <", Status.STATUS_BAD_REQUEST);
mapForCheck.put("html:vbscript:msgbox(\"XSS\")", Status.STATUS_BAD_REQUEST);
mapForCheck.put("<STYLE>@im\\port'\\ja\\vasc\\ript:alert(\"XSS\")';</STYLE>", Status.STATUS_BAD_REQUEST);
mapForCheck.put("<IMG SRC= onmouseover=\"alert('xxs')\">", Status.STATUS_BAD_REQUEST);
mapForCheck.put("BODY onload!#$%&()*~+-_.,:;?@[/|\\]^`=alert(\"XSS\")>", Status.STATUS_BAD_REQUEST);
mapForCheck.put("onload54(dd)fg`=df", Status.STATUS_BAD_REQUEST);
JSONObject link;
link = createLink(LINK_TITLE, "Link desc", LINK_URL, false, Status.STATUS_OK);
String name = getNameFromLink(link);
for (String url : mapForCheck.keySet())
{
int expStatus = mapForCheck.get(url);
updateLink(name, LINK_TITLE, "Link desc", url, false, expStatus);
}
}
/**
* Listing
*/