Repository: struts Updated Branches: refs/heads/master 82f61666f -> 4e9fa8423
WW-4768 Adds proper validation if request is a multipart request Project: http://git-wip-us.apache.org/repos/asf/struts/repo Commit: http://git-wip-us.apache.org/repos/asf/struts/commit/4e9fa842 Tree: http://git-wip-us.apache.org/repos/asf/struts/tree/4e9fa842 Diff: http://git-wip-us.apache.org/repos/asf/struts/diff/4e9fa842 Branch: refs/heads/master Commit: 4e9fa8423931417da8bc60ce220f46935b54c5de Parents: 82f6166 Author: Lukasz Lenart <lukaszlen...@apache.org> Authored: Thu Mar 23 08:07:21 2017 +0100 Committer: Lukasz Lenart <lukaszlen...@apache.org> Committed: Thu Mar 23 08:07:21 2017 +0100 ---------------------------------------------------------------------- .../org/apache/struts2/StrutsConstants.java | 2 ++ .../apache/struts2/dispatcher/Dispatcher.java | 38 ++++++++++++++++++-- .../struts2/dispatcher/DispatcherTest.java | 3 +- .../interceptor/FileUploadInterceptorTest.java | 4 +++ 4 files changed, 44 insertions(+), 3 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/struts/blob/4e9fa842/core/src/main/java/org/apache/struts2/StrutsConstants.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/struts2/StrutsConstants.java b/core/src/main/java/org/apache/struts2/StrutsConstants.java index a868edd..b41f7e6 100644 --- a/core/src/main/java/org/apache/struts2/StrutsConstants.java +++ b/core/src/main/java/org/apache/struts2/StrutsConstants.java @@ -139,6 +139,8 @@ public final class StrutsConstants { */ public static final String STRUTS_MULTIPART_PARSER = "struts.multipart.parser"; + public static final String STRUTS_MULTIPART_VALIDATION_REGEX = "struts.multipart.validationRegex"; + /** How Spring should autowire. Valid values are 'name', 'type', 'auto', and 'constructor' */ public static final String STRUTS_OBJECTFACTORY_SPRING_AUTOWIRE = "struts.objectFactory.spring.autoWire"; http://git-wip-us.apache.org/repos/asf/struts/blob/4e9fa842/core/src/main/java/org/apache/struts2/dispatcher/Dispatcher.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/struts2/dispatcher/Dispatcher.java b/core/src/main/java/org/apache/struts2/dispatcher/Dispatcher.java index b7714be..280e27b 100644 --- a/core/src/main/java/org/apache/struts2/dispatcher/Dispatcher.java +++ b/core/src/main/java/org/apache/struts2/dispatcher/Dispatcher.java @@ -66,6 +66,7 @@ import java.io.File; import java.io.IOException; import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.regex.Pattern; /** * A utility class the actual dispatcher delegates most of its tasks to. Each instance @@ -82,6 +83,13 @@ public class Dispatcher { private static final Logger LOG = LogManager.getLogger(Dispatcher.class); /** + * {@link HttpServletRequest#getMethod()} + */ + public static final String REQUEST_POST_METHOD = "POST"; + + public static final String MULTIPART_FORM_DATA_REGEX = "^multipart\\/form-data(; boundary=[a-zA-Z0-9]{1,70})?"; + + /** * Provide a thread local instance. */ private static ThreadLocal<Dispatcher> instance = new ThreadLocal<>(); @@ -122,6 +130,11 @@ public class Dispatcher { private String multipartHandlerName; /** + * A regular expression used to validate if request is a multipart/form-data request + */ + private Pattern multipartValidationPattern = Pattern.compile(MULTIPART_FORM_DATA_REGEX); + + /** * Provide list of default configuration files. */ private static final String DEFAULT_CONFIGURATION_PATHS = "struts-default.xml,struts-plugin.xml,struts.xml"; @@ -264,6 +277,11 @@ public class Dispatcher { multipartHandlerName = val; } + @Inject(value = StrutsConstants.STRUTS_MULTIPART_VALIDATION_REGEX, required = false) + public void setMultipartValidationRegex(String multipartValidationRegex) { + this.multipartValidationPattern = Pattern.compile(multipartValidationRegex); + } + @Inject public void setValueStackFactory(ValueStackFactory valueStackFactory) { this.valueStackFactory = valueStackFactory; @@ -781,8 +799,7 @@ public class Dispatcher { return request; } - String content_type = request.getContentType(); - if (content_type != null && content_type.contains("multipart/form-data")) { + if (isMultipartRequest(request)) { MultiPartRequest multiPartRequest = getMultiPartRequest(); LocaleProviderFactory localeProviderFactory = getContainer().getInstance(LocaleProviderFactory.class); @@ -801,6 +818,23 @@ public class Dispatcher { } /** + * Checks if request is a multipart request (a file upload request) + * + * @param request current servlet request + * @return true if it is a multipart request + * + * @since 2.5.11 + */ + protected boolean isMultipartRequest(HttpServletRequest request) { + String httpMethod = request.getMethod(); + String contentType = request.getContentType(); + + return REQUEST_POST_METHOD.equalsIgnoreCase(httpMethod) && + contentType != null && + multipartValidationPattern.matcher(contentType.toLowerCase(Locale.ENGLISH)).matches(); + } + + /** * On each request it must return a new instance as implementation could be not thread safe * and thus ensure of resource clean up * http://git-wip-us.apache.org/repos/asf/struts/blob/4e9fa842/core/src/test/java/org/apache/struts2/dispatcher/DispatcherTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/struts2/dispatcher/DispatcherTest.java b/core/src/test/java/org/apache/struts2/dispatcher/DispatcherTest.java index d8f10cc..ffa43e1 100644 --- a/core/src/test/java/org/apache/struts2/dispatcher/DispatcherTest.java +++ b/core/src/test/java/org/apache/struts2/dispatcher/DispatcherTest.java @@ -138,7 +138,8 @@ public class DispatcherTest extends StrutsInternalTestCase { MockHttpServletRequest req = new MockHttpServletRequest(); MockHttpServletResponse res = new MockHttpServletResponse(); - req.setContentType("multipart/form-data"); + req.setMethod("post"); + req.setContentType("multipart/form-data; boundary=asdcvb345asd"); Dispatcher du = initDispatcher(Collections.<String, String>emptyMap()); du.prepare(req, res); HttpServletRequest wrapped = du.wrapRequest(req); http://git-wip-us.apache.org/repos/asf/struts/blob/4e9fa842/core/src/test/java/org/apache/struts2/interceptor/FileUploadInterceptorTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/struts2/interceptor/FileUploadInterceptorTest.java b/core/src/test/java/org/apache/struts2/interceptor/FileUploadInterceptorTest.java index 8e68cef..a3e3143 100644 --- a/core/src/test/java/org/apache/struts2/interceptor/FileUploadInterceptorTest.java +++ b/core/src/test/java/org/apache/struts2/interceptor/FileUploadInterceptorTest.java @@ -229,6 +229,7 @@ public class FileUploadInterceptorTest extends StrutsInternalTestCase { req.setCharacterEncoding("text/html"); req.setContentType("text/xml"); // not a multipart contentype + req.setMethod("post"); req.addHeader("Content-type", "multipart/form-data"); MyFileupAction action = container.inject(MyFileupAction.class); @@ -249,6 +250,7 @@ public class FileUploadInterceptorTest extends StrutsInternalTestCase { MockHttpServletRequest req = new MockHttpServletRequest(); req.setCharacterEncoding("text/html"); + req.setMethod("post"); req.addHeader("Content-type", "multipart/form-data"); req.setContent(null); // there is no content @@ -269,6 +271,7 @@ public class FileUploadInterceptorTest extends StrutsInternalTestCase { public void testSuccessUploadOfATextFileMultipartRequest() throws Exception { MockHttpServletRequest req = new MockHttpServletRequest(); req.setCharacterEncoding("text/html"); + req.setMethod("post"); req.addHeader("Content-type", "multipart/form-data; boundary=---1234"); // inspired by the unit tests for jakarta commons fileupload @@ -374,6 +377,7 @@ public class FileUploadInterceptorTest extends StrutsInternalTestCase { public void testMultipartRequestLocalizedError() throws Exception { MockHttpServletRequest req = new MockHttpServletRequest(); req.setCharacterEncoding("text/html"); + req.setMethod("post"); req.addHeader("Content-type", "multipart/form-data; boundary=---1234"); // inspired by the unit tests for jakarta commons fileupload