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

84047: Merged V4.2-BUG-FIX (4.2.4) to HEAD-BUG-FIX (5.0/Cloud)
      83804: Merged V4.1-BUG-FIX (4.1.10) to V4.2-BUG-FIX (4.2.4)
         83716: MNT-11355: Merged DEV to V4.1-BUG-FIX (4.1.10)
            83675: MNT-11355: PostSubParamInjection and HeaderParamManipulation attacks identified by HP Web Inspect
               - Hide SQLException from stacktrace that is sent in error response to client. Add unit test.
         -----------------------------------------------
         M /alfresco/BRANCHES/DEV/V4.1-BUG-FIX
         M /alfresco/BRANCHES/DEV/V4.1-BUG-FIX/root/projects/core/source/java/org/alfresco/error/AlfrescoRuntimeException.java
         M /alfresco/BRANCHES/DEV/V4.1-BUG-FIX/root/projects/remote-api/source/java/org/alfresco/repo/web/scripts/RepositoryContainer.java
         M /alfresco/BRANCHES/DEV/V4.1-BUG-FIX/root/projects/remote-api/source/test-java/org/alfresco/repo/web/scripts/RepositoryContainerTest.java


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@84623 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Alan Davis
2014-09-18 17:21:26 +00:00
parent 3483a48613
commit bff1e27004
2 changed files with 162 additions and 4 deletions

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2005-2013 Alfresco Software Limited. * Copyright (C) 2005-2014 Alfresco Software Limited.
* *
* This file is part of Alfresco * This file is part of Alfresco
* *
@@ -21,6 +21,7 @@ package org.alfresco.repo.web.scripts;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.SocketException; import java.net.SocketException;
import java.sql.SQLException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@@ -28,6 +29,7 @@ import javax.servlet.http.HttpServletResponse;
import javax.transaction.Status; import javax.transaction.Status;
import javax.transaction.UserTransaction; import javax.transaction.UserTransaction;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.error.ExceptionStackUtil; import org.alfresco.error.ExceptionStackUtil;
import org.alfresco.repo.model.Repository; import org.alfresco.repo.model.Repository;
import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil;
@@ -88,6 +90,8 @@ public class RepositoryContainer extends AbstractRuntimeContainer
private long maxContentSize = (long) 4 * 1024 * 1024 * 1024; // 4gb private long maxContentSize = (long) 4 * 1024 * 1024 * 1024; // 4gb
private ThresholdOutputStreamFactory streamFactory = null; private ThresholdOutputStreamFactory streamFactory = null;
private final static Class<?>[] HIDE_EXCEPTIONS = new Class[] { SQLException.class };
/* /*
* Shame init is already used (by TenantRepositoryContainer). * Shame init is already used (by TenantRepositoryContainer).
*/ */
@@ -252,10 +256,43 @@ public class RepositoryContainer extends AbstractRuntimeContainer
public void executeScript(WebScriptRequest scriptReq, WebScriptResponse scriptRes, final Authenticator auth) public void executeScript(WebScriptRequest scriptReq, WebScriptResponse scriptRes, final Authenticator auth)
throws IOException throws IOException
{ {
final boolean debug = logger.isDebugEnabled(); try
{
executeScriptInternal(scriptReq, scriptRes, auth);
}
catch (RuntimeException e)
{
Throwable hideCause = ExceptionStackUtil.getCause(e, HIDE_EXCEPTIONS);
if (hideCause != null)
{
AlfrescoRuntimeException alf = null;
if (e instanceof AlfrescoRuntimeException)
{
alf = (AlfrescoRuntimeException) e;
}
else
{
// The message will not have a numerical identifier
alf = new AlfrescoRuntimeException("WebScript execution failed", e);
}
String num = alf.getNumericalId();
logger.error("Server error (" + num + ")", e);
throw new RuntimeException("Server error (" + num + "). Details can be found in the server logs.");
}
else
{
throw e;
}
}
}
protected void executeScriptInternal(WebScriptRequest scriptReq, WebScriptResponse scriptRes, final Authenticator auth)
throws IOException
{
final WebScript script = scriptReq.getServiceMatch().getWebScript(); final WebScript script = scriptReq.getServiceMatch().getWebScript();
final Description desc = script.getDescription(); final Description desc = script.getDescription();
final boolean debug = logger.isDebugEnabled();
// Escalate the webscript declared level of authentication to the container required authentication // Escalate the webscript declared level of authentication to the container required authentication
// eg. must be guest if MT is enabled unless credentials are empty // eg. must be guest if MT is enabled unless credentials are empty
RequiredAuthentication containerRequiredAuthentication = getRequiredAuthentication(); RequiredAuthentication containerRequiredAuthentication = getRequiredAuthentication();

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2005-2010 Alfresco Software Limited. * Copyright (C) 2005-2014 Alfresco Software Limited.
* *
* This file is part of Alfresco * This file is part of Alfresco
* *
@@ -18,19 +18,37 @@
*/ */
package org.alfresco.repo.web.scripts; package org.alfresco.repo.web.scripts;
import java.sql.SQLException;
import java.util.regex.Pattern;
import java.util.Arrays; import java.util.Arrays;
import javax.servlet.http.HttpServletResponse;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.error.ExceptionStackUtil;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.repo.forms.FormException;
import org.alfresco.repo.security.authentication.AuthenticationComponent; import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.service.cmr.security.MutableAuthenticationService; import org.alfresco.service.cmr.security.MutableAuthenticationService;
import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.util.CronTriggerBean; import org.alfresco.util.CronTriggerBean;
import org.alfresco.util.PropertyMap; import org.alfresco.util.PropertyMap;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.extensions.webscripts.TestWebScriptServer.GetRequest; import org.springframework.extensions.webscripts.TestWebScriptServer.GetRequest;
import org.springframework.extensions.webscripts.TestWebScriptServer.PutRequest; import org.springframework.extensions.webscripts.TestWebScriptServer.PutRequest;
import org.springframework.extensions.webscripts.TestWebScriptServer.Response; import org.springframework.extensions.webscripts.TestWebScriptServer.Response;
import org.springframework.extensions.webscripts.Authenticator;
import org.springframework.extensions.webscripts.WebScriptException;
import org.springframework.extensions.webscripts.WebScriptRequest;
import org.springframework.extensions.webscripts.WebScriptResponse;
import static org.springframework.extensions.webscripts.Status.*; import static org.springframework.extensions.webscripts.Status.*;
import static org.mockito.Matchers.any;
/** /**
* Unit test to test runas function * Unit test to test runas function
* *
@@ -143,4 +161,107 @@ public class RepositoryContainerTest extends BaseWebScriptTest
response = sendRequest(new PutRequest("/test/largecontenttest", content, "text/plain"), STATUS_OK); response = sendRequest(new PutRequest("/test/largecontenttest", content, "text/plain"), STATUS_OK);
assertEquals(SUCCESS, response.getContentAsString()); assertEquals(SUCCESS, response.getContentAsString());
} }
public void testHideExceptions() throws Exception
{
final Pattern patternHiddenException = Pattern.compile("Server error \\(\\d{8}\\)\\. Details can be found in the server logs\\.");
final String messageFormException = "Failed to persist field 'prop_cm_name'";
final String messageAuthenticationException = "Authentication failed for Web Script";
RepositoryContainer repoContainer = (RepositoryContainer) getServer().getApplicationContext().getBean("webscripts.container");
RepositoryContainer repoContainerMock = Mockito.spy(repoContainer);
// case: AlfrescoRuntimeException with SQLException cause
Mockito.doAnswer(new Answer<Object>()
{
public Object answer(InvocationOnMock invocation)
{
throw new AlfrescoRuntimeException("AlfrescoRuntimeException", new SQLException("SQLException"));
}
}).when(repoContainerMock).executeScriptInternal(any(WebScriptRequest.class), any(WebScriptResponse.class), any(Authenticator.class));
try
{
repoContainerMock.executeScript(null, null, null);
}
catch (Exception e)
{
assertNull("SQLException cause should be hidden for client", ExceptionStackUtil.getCause(e, new Class[] { SQLException.class }));
assertTrue("Details should be in the server logs.", patternHiddenException.matcher(e.getMessage()).matches());
}
// case: AlfrescoRuntimeException with NOT SQLException cause
Mockito.doAnswer(new Answer<Object>()
{
public Object answer(InvocationOnMock invocation)
{
throw new AlfrescoRuntimeException("AlfrescoRuntimeException", new NullPointerException());
}
}).when(repoContainerMock).executeScriptInternal(any(WebScriptRequest.class), any(WebScriptResponse.class), any(Authenticator.class));
try
{
repoContainerMock.executeScript(null, null, null);
}
catch (Exception e)
{
assertNotNull("NullPointerException cause should be visible for client", ExceptionStackUtil.getCause(e, new Class[] { NullPointerException.class }));
assertFalse("Details should be available for client", patternHiddenException.matcher(e.getMessage()).matches());
}
// case: RuntimeException with SQLException cause
Mockito.doAnswer(new Answer<Object>()
{
public Object answer(InvocationOnMock invocation)
{
throw new RuntimeException("AlfrescoRuntimeException", new SQLException("SQLException"));
}
}).when(repoContainerMock).executeScriptInternal(any(WebScriptRequest.class), any(WebScriptResponse.class), any(Authenticator.class));
try
{
repoContainerMock.executeScript(null, null, null);
}
catch (Exception e)
{
assertNull("SQLException cause should be hidden for client", ExceptionStackUtil.getCause(e, new Class[] { SQLException.class }));
assertTrue("Details should be in the server logs.", patternHiddenException.matcher(e.getMessage()).matches());
}
// case: FormException
Mockito.doAnswer(new Answer<Object>()
{
public Object answer(InvocationOnMock invocation)
{
throw new FormException(messageFormException);
}
}).when(repoContainerMock).executeScriptInternal(any(WebScriptRequest.class), any(WebScriptResponse.class), any(Authenticator.class));
try
{
repoContainerMock.executeScript(null, null, null);
}
catch (Exception e)
{
assertTrue("FormException should be visible for client", e instanceof FormException);
assertFalse("Details should be available for client", patternHiddenException.matcher(e.getMessage()).matches());
assertTrue("Actual message should be available for client", e.getMessage().contains(messageFormException));
}
// case: WebScriptException
Mockito.doAnswer(new Answer<Object>()
{
public Object answer(InvocationOnMock invocation)
{
throw new WebScriptException(HttpServletResponse.SC_UNAUTHORIZED, messageAuthenticationException);
}
}).when(repoContainerMock).executeScriptInternal(any(WebScriptRequest.class), any(WebScriptResponse.class), any(Authenticator.class));
try
{
repoContainerMock.executeScript(null, null, null);
}
catch (Exception e)
{
assertTrue("WebScriptException should be visible for client", e instanceof WebScriptException);
assertFalse("Details should be available for client", patternHiddenException.matcher(e.getMessage()).matches());
assertTrue("Actual message should be available for client", e.getMessage().contains(messageAuthenticationException));
}
}
} }