This is an automated email from the ASF dual-hosted git repository. yasserzamani pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/struts.git
The following commit(s) were added to refs/heads/master by this push: new 5a67d58b7 add some improvements 5a67d58b7 is described below commit 5a67d58b757426376943b084fec89b121392a24c Author: Yasser Zamani <yasserzam...@apache.org> AuthorDate: Sun Jun 4 14:22:49 2023 +0430 add some improvements --- .../ognl/accessor/XWorkListPropertyAccessor.java | 5 ++ .../java/org/apache/struts2/StrutsConstants.java | 3 + .../struts2/config/entities/ConstantConfig.java | 10 ++++ .../multipart/AbstractMultiPartRequest.java | 10 ++++ .../multipart/JakartaMultiPartRequest.java | 13 ++++- .../org/apache/struts2/default.properties | 1 + .../org/apache/struts2/struts-messages.properties | 1 + .../accessor/XWorkListPropertyAccessorTest.java | 16 +++--- .../interceptor/FileUploadInterceptorTest.java | 64 ++++++++++++++++++++-- 9 files changed, 111 insertions(+), 12 deletions(-) diff --git a/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkListPropertyAccessor.java b/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkListPropertyAccessor.java index ece1a9248..47a640438 100644 --- a/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkListPropertyAccessor.java +++ b/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkListPropertyAccessor.java @@ -109,6 +109,11 @@ public class XWorkListPropertyAccessor extends ListPropertyAccessor { if (listSize <= index) { Object result; + if (index > autoGrowCollectionLimit) { + throw new OgnlException("Error auto growing collection size to " + index + " which limited to " + + autoGrowCollectionLimit); + } + for (int i = listSize; i < index; i++) { list.add(null); } diff --git a/core/src/main/java/org/apache/struts2/StrutsConstants.java b/core/src/main/java/org/apache/struts2/StrutsConstants.java index e53bb2322..29dfca4b9 100644 --- a/core/src/main/java/org/apache/struts2/StrutsConstants.java +++ b/core/src/main/java/org/apache/struts2/StrutsConstants.java @@ -145,6 +145,9 @@ public final class StrutsConstants { /** The maximum number of files allowed in a multipart request */ public static final String STRUTS_MULTIPART_MAXFILES = "struts.multipart.maxFiles"; + /** The maximum length of a string parameter in a multipart request. */ + public static final String STRUTS_MULTIPART_MAX_STRING_LENGTH = "struts.multipart.maxStringLength"; + /** The maximum size per file in a multipart request */ public static final String STRUTS_MULTIPART_MAXFILESIZE = "struts.multipart.maxFileSize"; /** The directory to use for storing uploaded files */ diff --git a/core/src/main/java/org/apache/struts2/config/entities/ConstantConfig.java b/core/src/main/java/org/apache/struts2/config/entities/ConstantConfig.java index 73f22013c..2b854243d 100644 --- a/core/src/main/java/org/apache/struts2/config/entities/ConstantConfig.java +++ b/core/src/main/java/org/apache/struts2/config/entities/ConstantConfig.java @@ -66,6 +66,7 @@ public class ConstantConfig { private Long multipartMaxSize; private Long multipartMaxFiles; private Long multipartMaxFileSize; + private Long multipartMaxStringLength; private String multipartSaveDir; private Integer multipartBufferSize; private BeanConfig multipartParser; @@ -201,6 +202,7 @@ public class ConstantConfig { map.put(StrutsConstants.STRUTS_MULTIPART_MAXSIZE, Objects.toString(multipartMaxSize, null)); map.put(StrutsConstants.STRUTS_MULTIPART_MAXFILES, Objects.toString(multipartMaxFiles, null)); map.put(StrutsConstants.STRUTS_MULTIPART_MAXFILESIZE, Objects.toString(multipartMaxFileSize, null)); + map.put(StrutsConstants.STRUTS_MULTIPART_MAX_STRING_LENGTH, Objects.toString(multipartMaxStringLength, null)); map.put(StrutsConstants.STRUTS_MULTIPART_SAVEDIR, multipartSaveDir); map.put(StrutsConstants.STRUTS_MULTIPART_BUFFERSIZE, Objects.toString(multipartBufferSize, null)); map.put(StrutsConstants.STRUTS_MULTIPART_PARSER, beanConfToString(multipartParser)); @@ -602,6 +604,14 @@ public class ConstantConfig { this.multipartMaxFileSize = multipartMaxFileSize; } + public Long getMultipartMaxStringLength() { + return multipartMaxStringLength; + } + + public void setMultipartMaxStringLength(Long multipartMaxStringLength) { + this.multipartMaxStringLength = multipartMaxStringLength; + } + public String getMultipartSaveDir() { return multipartSaveDir; } diff --git a/core/src/main/java/org/apache/struts2/dispatcher/multipart/AbstractMultiPartRequest.java b/core/src/main/java/org/apache/struts2/dispatcher/multipart/AbstractMultiPartRequest.java index e8806d370..23d879ba4 100644 --- a/core/src/main/java/org/apache/struts2/dispatcher/multipart/AbstractMultiPartRequest.java +++ b/core/src/main/java/org/apache/struts2/dispatcher/multipart/AbstractMultiPartRequest.java @@ -58,6 +58,11 @@ public abstract class AbstractMultiPartRequest implements MultiPartRequest { */ protected Long maxFiles; + /** + * Specifies the maximum length of a string parameter in a multipart request. + */ + protected Long maxStringLength; + /** * Specifies the maximum size per file in the request. */ @@ -106,6 +111,11 @@ public abstract class AbstractMultiPartRequest implements MultiPartRequest { this.maxFileSize = Long.parseLong(maxFileSize); } + @Inject(StrutsConstants.STRUTS_MULTIPART_MAX_STRING_LENGTH) + public void setMaxStringLength(String maxStringLength) { + this.maxStringLength = Long.parseLong(maxStringLength); + } + @Inject public void setLocaleProviderFactory(LocaleProviderFactory localeProviderFactory) { defaultLocale = localeProviderFactory.createLocaleProvider().getLocale(); diff --git a/core/src/main/java/org/apache/struts2/dispatcher/multipart/JakartaMultiPartRequest.java b/core/src/main/java/org/apache/struts2/dispatcher/multipart/JakartaMultiPartRequest.java index 551e1d51c..cd0aaff96 100644 --- a/core/src/main/java/org/apache/struts2/dispatcher/multipart/JakartaMultiPartRequest.java +++ b/core/src/main/java/org/apache/struts2/dispatcher/multipart/JakartaMultiPartRequest.java @@ -140,8 +140,19 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest { values = new ArrayList<>(); } - if (item.getSize() == 0) { + long size = item.getSize(); + if (size == 0) { values.add(StringUtils.EMPTY); + } else if (size > maxStringLength) { + String errorKey = "struts.messages.upload.error.parameter.too.long"; + LocalizedMessage localizedMessage = new LocalizedMessage(this.getClass(), errorKey, null, + new Object[] { item.getFieldName(), maxStringLength, size }); + + if (!errors.contains(localizedMessage)) { + errors.add(localizedMessage); + } + return; + } else if (charset != null) { values.add(item.getString(charset)); } else { diff --git a/core/src/main/resources/org/apache/struts2/default.properties b/core/src/main/resources/org/apache/struts2/default.properties index c516604bf..56bc714de 100644 --- a/core/src/main/resources/org/apache/struts2/default.properties +++ b/core/src/main/resources/org/apache/struts2/default.properties @@ -69,6 +69,7 @@ struts.multipart.parser=jakarta struts.multipart.saveDir= struts.multipart.maxSize=2097152 struts.multipart.maxFiles=256 +struts.multipart.maxStringLength=4096 # struts.multipart.maxFileSize= ### Load custom property files (does not override struts.properties!) diff --git a/core/src/main/resources/org/apache/struts2/struts-messages.properties b/core/src/main/resources/org/apache/struts2/struts-messages.properties index 1d8f2a7a9..57b7e2b54 100644 --- a/core/src/main/resources/org/apache/struts2/struts-messages.properties +++ b/core/src/main/resources/org/apache/struts2/struts-messages.properties @@ -26,6 +26,7 @@ struts.messages.invalid.content.type=Could not find a Content-Type for {0}. Veri struts.messages.removing.file=Removing file {0} {1} struts.messages.error.uploading=Error uploading: {0} struts.messages.error.file.too.large=File {0} is too large to be uploaded. Maximum allowed size is {4} bytes! +struts.messages.upload.error.parameter.too.long=The request parameter "{0}" was too long. Max length allowed is {1}, but found {2}! struts.messages.error.content.type.not.allowed=Content-Type not allowed: {0} "{1}" "{2}" {3} struts.messages.error.file.extension.not.allowed=File extension not allowed: {0} "{1}" "{2}" {3} diff --git a/core/src/test/java/com/opensymphony/xwork2/ognl/accessor/XWorkListPropertyAccessorTest.java b/core/src/test/java/com/opensymphony/xwork2/ognl/accessor/XWorkListPropertyAccessorTest.java index 66c716c2c..4a89d8935 100644 --- a/core/src/test/java/com/opensymphony/xwork2/ognl/accessor/XWorkListPropertyAccessorTest.java +++ b/core/src/test/java/com/opensymphony/xwork2/ognl/accessor/XWorkListPropertyAccessorTest.java @@ -22,7 +22,7 @@ import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.XWorkTestCase; import com.opensymphony.xwork2.util.ListHolder; import com.opensymphony.xwork2.util.ValueStack; -import ognl.ListPropertyAccessor; +import com.opensymphony.xwork2.util.reflection.ReflectionContextState; import ognl.PropertyAccessor; import java.util.ArrayList; @@ -42,11 +42,11 @@ public class XWorkListPropertyAccessorTest extends XWorkTestCase { assertNotNull(listHolder.getLongs()); assertEquals(3, listHolder.getLongs().size()); - assertEquals(new Long(1), (Long) listHolder.getLongs().get(0)); - assertEquals(new Long(2), (Long) listHolder.getLongs().get(1)); - assertEquals(new Long(3), (Long) listHolder.getLongs().get(2)); + assertEquals(new Long(1), listHolder.getLongs().get(0)); + assertEquals(new Long(2), listHolder.getLongs().get(1)); + assertEquals(new Long(3), listHolder.getLongs().get(2)); - assertTrue(((Boolean) vs.findValue("longs.contains(1)")).booleanValue()); + assertTrue((Boolean) vs.findValue("longs.contains(1)")); } public void testCanAccessListSizeProperty() { @@ -60,8 +60,8 @@ public class XWorkListPropertyAccessorTest extends XWorkTestCase { vs.push(listHolder); - assertEquals(new Integer(myList.size()), vs.findValue("strings.size()")); - assertEquals(new Integer(myList.size()), vs.findValue("strings.size")); + assertEquals(myList.size(), vs.findValue("strings.size()")); + assertEquals(myList.size(), vs.findValue("strings.size")); } public void testAutoGrowthCollectionLimit() { @@ -73,12 +73,14 @@ public class XWorkListPropertyAccessorTest extends XWorkTestCase { listHolder.setStrings(myList); ValueStack vs = ActionContext.getContext().getValueStack(); + ReflectionContextState.setCreatingNullObjects(vs.getContext(), true); vs.push(listHolder); vs.setValue("strings[0]", "a"); vs.setValue("strings[1]", "b"); vs.setValue("strings[2]", "c"); vs.setValue("strings[3]", "d"); + vs.findValue("strings[3]"); assertEquals(3, vs.findValue("strings.size()")); } 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 0e6874086..b9bab88d4 100644 --- a/core/src/test/java/org/apache/struts2/interceptor/FileUploadInterceptorTest.java +++ b/core/src/test/java/org/apache/struts2/interceptor/FileUploadInterceptorTest.java @@ -452,6 +452,55 @@ public class FileUploadInterceptorTest extends StrutsInternalTestCase { msg); } + public void testMultipartRequestMaxStringLength() throws Exception { + MockHttpServletRequest req = new MockHttpServletRequest(); + req.setCharacterEncoding(StandardCharsets.UTF_8.name()); + req.setMethod("post"); + req.addHeader("Content-type", "multipart/form-data; boundary=---1234"); + + // inspired by the unit tests for jakarta commons fileupload + String content = ("-----1234\r\n" + + "Content-Disposition: form-data; name=\"file\"; filename=\"deleteme.txt\"\r\n" + + "Content-Type: text/html\r\n" + + "\r\n" + + "Unit test of FileUploadInterceptor" + + "\r\n" + + "-----1234\r\n" + + "Content-Disposition: form-data; name=\"normalFormField1\"\r\n" + + "\r\n" + + "it works" + + "\r\n" + + "-----1234\r\n" + + "Content-Disposition: form-data; name=\"normalFormField2\"\r\n" + + "\r\n" + + "long string should not work" + + "\r\n" + + "-----1234--\r\n"); + req.setContent(content.getBytes(StandardCharsets.US_ASCII)); + + MyFileupAction action = container.inject(MyFileupAction.class); + + MockActionInvocation mai = new MockActionInvocation(); + mai.setAction(action); + mai.setResultCode("success"); + mai.setInvocationContext(ActionContext.getContext()); + Map<String, Object> param = new HashMap<>(); + ActionContext.getContext() + .withParameters(HttpParameters.create(param).build()) + .withServletRequest(createMultipartRequestMaxStringLength(req, 20)); + + interceptor.intercept(mai); + + assertTrue(action.hasActionErrors()); + + Collection<String> errors = action.getActionErrors(); + assertEquals(1, errors.size()); + String msg = errors.iterator().next(); + assertEquals( + "The request parameter \"normalFormField2\" was too long. Max length allowed is 20, but found 27!", + msg); + } + public void testMultipartRequestLocalizedError() throws Exception { MockHttpServletRequest req = new MockHttpServletRequest(); req.setCharacterEncoding(StandardCharsets.UTF_8.name()); @@ -512,22 +561,29 @@ public class FileUploadInterceptorTest extends StrutsInternalTestCase { } private MultiPartRequestWrapper createMultipartRequestMaxFileSize(HttpServletRequest req, int maxfilesize) throws IOException { - return createMultipartRequest(req, -1, maxfilesize, -1); + return createMultipartRequest(req, -1, maxfilesize, -1, -1); } private MultiPartRequestWrapper createMultipartRequestMaxFiles(HttpServletRequest req, int maxfiles) throws IOException { - return createMultipartRequest(req, -1, -1, maxfiles); + return createMultipartRequest(req, -1, -1, maxfiles, -1); } private MultiPartRequestWrapper createMultipartRequestMaxSize(HttpServletRequest req, int maxsize) throws IOException { - return createMultipartRequest(req, maxsize, -1, -1); + return createMultipartRequest(req, maxsize, -1, -1, -1); } - private MultiPartRequestWrapper createMultipartRequest(HttpServletRequest req, int maxsize, int maxfilesize, int maxfiles) throws IOException { + private MultiPartRequestWrapper createMultipartRequestMaxStringLength(HttpServletRequest req, int maxStringLength) throws IOException { + return createMultipartRequest(req, -1, -1, -1, maxStringLength); + } + + private MultiPartRequestWrapper createMultipartRequest(HttpServletRequest req, int maxsize, int maxfilesize, + int maxfiles, int maxStringLength) throws IOException { + JakartaMultiPartRequest jak = new JakartaMultiPartRequest(); jak.setMaxSize(String.valueOf(maxsize)); jak.setMaxFileSize(String.valueOf(maxfilesize)); jak.setMaxFiles(String.valueOf(maxfiles)); + jak.setMaxStringLength(String.valueOf(maxStringLength)); return new MultiPartRequestWrapper(jak, req, tempDir.getAbsolutePath(), new DefaultLocaleProvider()); }