This is an automated email from the ASF dual-hosted git repository.

lukaszlenart pushed a commit to branch WW-5474-multipart-maxfiles-semantics
in repository https://gitbox.apache.org/repos/asf/struts.git

commit ed74a27749290f6a4e1970a1e8f9d37e499e1446
Author: Lukasz Lenart <[email protected]>
AuthorDate: Wed Jul 22 15:34:01 2026 +0200

    WW-5474 fix(multipart): honor -1 unlimited sentinel in total-parts backstop
    
    prepareServletFileUpload applied the total-parts backstop whenever both
    maxFiles and maxParameterCount were non-null, without checking for the
    -1 "unlimited" sentinel already honored by 
enforceMaxFiles/enforceMaxParameterCount.
    With maxFiles=-1 and maxParameterCount=256, maxParts computed to 255 and
    was passed to commons-fileupload2's setMaxFileCount (which counts ALL
    parts), wrongly rejecting large file-only uploads. Only apply the
    backstop when both limits are finite (non-null and >= 0).
    
    Co-Authored-By: Claude Opus 4.8 <[email protected]>
---
 .../multipart/AbstractMultiPartRequest.java        |  2 +-
 .../multipart/JakartaMultiPartRequestTest.java     | 25 ++++++++++++++++++++++
 2 files changed, 26 insertions(+), 1 deletion(-)

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 0c5e618bd..17fadc035 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
@@ -239,7 +239,7 @@ public abstract class AbstractMultiPartRequest implements 
MultiPartRequest {
             LOG.debug("Applies max size: {} to file upload request", maxSize);
             servletFileUpload.setMaxSize(maxSize);
         }
-        if (maxFiles != null && maxParameterCount != null) {
+        if (maxFiles != null && maxFiles >= 0 && maxParameterCount != null && 
maxParameterCount >= 0) {
             long maxParts = maxFiles + maxParameterCount;
             LOG.debug("Applies total parts backstop: {} to file upload 
request", maxParts);
             servletFileUpload.setMaxFileCount(maxParts);
diff --git 
a/core/src/test/java/org/apache/struts2/dispatcher/multipart/JakartaMultiPartRequestTest.java
 
b/core/src/test/java/org/apache/struts2/dispatcher/multipart/JakartaMultiPartRequestTest.java
index 641388ff7..b80d45760 100644
--- 
a/core/src/test/java/org/apache/struts2/dispatcher/multipart/JakartaMultiPartRequestTest.java
+++ 
b/core/src/test/java/org/apache/struts2/dispatcher/multipart/JakartaMultiPartRequestTest.java
@@ -521,4 +521,29 @@ public class JakartaMultiPartRequestTest extends 
AbstractMultiPartRequestTest {
                 .isEqualTo("valid file content");
     }
 
+    @Test
+    public void unlimitedMaxFilesIsNotClampedByTotalPartsBackstop() throws 
IOException {
+        // Regression for WW-5474: maxFiles=-1 (unlimited) combined with a 
finite
+        // maxParameterCount must not compute a finite total-parts backstop
+        // (maxFiles + maxParameterCount) and pass it to commons-fileupload2's
+        // setMaxFileCount, which counts ALL parts. Before the fix, -1 + 256 = 
255
+        // wrongly rejected a 300-file/0-field upload at part 256.
+        int fileCount = 300;
+        StringBuilder content = new StringBuilder();
+        for (int i = 0; i < fileCount; i++) {
+            content.append(formFile("file" + i, "test" + i + ".csv", 
"1,2,3,4"));
+        }
+        content.append(endline).append("--").append(boundary).append("--");
+        
mockRequest.setContent(content.toString().getBytes(StandardCharsets.UTF_8));
+
+        multiPart.setMaxSize("-1"); // isolate: don't let the size backstop 
interfere
+        multiPart.setMaxFiles("-1"); // unlimited files
+        multiPart.setMaxParameterCount("256"); // finite, but must not clamp 
file count
+
+        multiPart.parse(mockRequest, tempDir);
+
+        assertThat(multiPart.getErrors()).isEmpty();
+        
assertThat(multiPart.getFileParameterNames().asIterator()).toIterable().hasSize(fileCount);
+    }
+
 }

Reply via email to