[ 
https://issues.apache.org/jira/browse/WW-5474?focusedWorklogId=1031703&page=com.atlassian.jira.plugin.system.issuetabpanels:worklog-tabpanel#worklog-1031703
 ]

ASF GitHub Bot logged work on WW-5474:
--------------------------------------

                Author: ASF GitHub Bot
            Created on: 22/Jul/26 14:57
            Start Date: 22/Jul/26 14:57
    Worklog Time Spent: 10m 
      Work Description: Copilot commented on code in PR #1806:
URL: https://github.com/apache/struts/pull/1806#discussion_r3631310969


##########
core/src/test/java/org/apache/struts2/dispatcher/multipart/JakartaStreamMultiPartRequestTest.java:
##########
@@ -216,14 +216,63 @@ public void exceedsMaxFilesPath() throws IOException {
         // when - set max files to 1
         multiPart.setMaxFiles("1");
         multiPart.parse(mockRequest, tempDir);
-        
-        // then - should have only 1 file and errors for others
-        assertThat(multiPart.uploadedFiles).hasSize(1);
+
+        // then - fail-closed: no partial files, one error
+        assertThat(multiPart.uploadedFiles).isEmpty();
         assertThat(multiPart.getErrors())
-                .isNotEmpty()
-                .allSatisfy(error -> 
-                    
assertThat(error.getTextKey()).isEqualTo("struts.messages.upload.error.FileUploadFileCountLimitException")
-                );
+                .map(LocalizedMessage::getTextKey)
+                
.containsExactly("struts.messages.upload.error.FileUploadFileCountLimitException");
+    }
+
+    @Test
+    public void streamManyFormFieldsWithFewFilesAreAccepted() throws 
IOException {
+        StringBuilder content = new StringBuilder();
+        for (int i = 0; i < 10; i++) {
+            content.append(formField("field" + i, "value" + i));
+        }
+        content.append(formFile("file1", "test1.csv", "1,2,3,4"));
+        content.append(endline).append("--").append(boundary).append("--");
+        
mockRequest.setContent(content.toString().getBytes(StandardCharsets.UTF_8));
+
+        multiPart.setMaxFiles("1");
+        multiPart.parse(mockRequest, tempDir);
+
+        assertThat(multiPart.getErrors()).isEmpty();
+        assertThat(multiPart.getFileParameterNames().asIterator()).toIterable()
+                
.asInstanceOf(InstanceOfAssertFactories.LIST).containsOnly("file1");

Review Comment:
   `toIterable()` returns an `Iterable` wrapper around the iterator, not a 
`List`. Casting it via `asInstanceOf(InstanceOfAssertFactories.LIST)` is likely 
to fail at runtime (ClassCastException). You can assert the contents directly 
on the `IterableAssert`.



##########
core/src/main/java/org/apache/struts2/dispatcher/multipart/JakartaMultiPartRequest.java:
##########
@@ -115,18 +115,31 @@ protected void processUpload(HttpServletRequest request, 
String saveDir) throws
 
         RequestContext requestContext = createRequestContext(request);
         
-        for (DiskFileItem item : 
servletFileUpload.parseRequest(requestContext)) {
-            // Track all DiskFileItem instances for cleanup - this is critical 
for security
-            // as it ensures temporary files are properly cleaned up even if 
processing fails
-            diskFileItems.add(item);
-
+        int fileCount = 0;
+        int parameterCount = 0;
+        // parseRequest() fully materializes every part (spilling large ones 
to disk) before we
+        // iterate, so register them all for cleanup up front - otherwise a 
fail-closed breach
+        // mid-loop would leak the temp files of every part after the 
breaching one.
+        List<DiskFileItem> items = 
servletFileUpload.parseRequest(requestContext);
+        diskFileItems.addAll(items);
+        for (DiskFileItem item : items) {
             LOG.debug(() -> "Processing a form field: " + 
normalizeSpace(item.getFieldName()));

Review Comment:
   This log line runs for both form fields and file parts, but always says 
"Processing a form field". For file parts it becomes misleading (and you 
already log "Processing a file" in the else-branch). Consider making this 
message neutral to avoid confusing debug output.



##########
core/src/test/java/org/apache/struts2/dispatcher/multipart/JakartaMultiPartRequestTest.java:
##########
@@ -422,6 +422,74 @@ public void errorDuplicationPrevention() throws 
IOException {
         assertThat(multiPartRequest.getErrors()).hasSize(1);
     }
 
+    @Test
+    public void manyFormFieldsWithFewFilesAreAccepted() throws IOException {
+        // Regression for WW-5474: maxFiles must not count form fields.
+        StringBuilder content = new StringBuilder();
+        for (int i = 0; i < 10; i++) {
+            content.append(formField("field" + i, "value" + i));
+        }
+        content.append(formFile("file1", "test1.csv", "1,2,3,4"));
+        content.append(formFile("file2", "test2.csv", "5,6,7,8"));
+        content.append(endline).append("--").append(boundary).append("--");
+        
mockRequest.setContent(content.toString().getBytes(StandardCharsets.UTF_8));
+
+        multiPart.setMaxFiles("2"); // only 2 files, but 10 fields present
+        multiPart.parse(mockRequest, tempDir);
+
+        assertThat(multiPart.getErrors()).isEmpty();
+        assertThat(multiPart.getFileParameterNames().asIterator()).toIterable()
+                
.asInstanceOf(InstanceOfAssertFactories.LIST).containsOnly("file1", "file2");

Review Comment:
   `toIterable()` returns an `Iterable` wrapper around the iterator, not a 
`List`. Casting it via `asInstanceOf(InstanceOfAssertFactories.LIST)` is likely 
to fail at runtime (ClassCastException). You can assert the contents directly 
on the `IterableAssert`.





Issue Time Tracking
-------------------

    Worklog Id:     (was: 1031703)
    Time Spent: 1h  (was: 50m)

> struts.multipart.maxFiles does not work as described/expected
> -------------------------------------------------------------
>
>                 Key: WW-5474
>                 URL: https://issues.apache.org/jira/browse/WW-5474
>             Project: Struts 2
>          Issue Type: Bug
>    Affects Versions: 6.3.0
>            Reporter: nikos dimitrakas
>            Priority: Major
>             Fix For: 7.3.0
>
>          Time Spent: 1h
>  Remaining Estimate: 0h
>
> According to the documentation the property struts.multipart.maxFiles (which 
> defaults to 256) is supposed to set a limit to how many files a multi-part 
> form submission may include. But in reality, the specified value sets a limit 
> to how many parameter values the form may submit, including strings, 
> integers, booleans (from all input fields, checkboxes, hiddens, etc).
> The problem is in the method JakartaMultiPartRequest.parsePrequest (part of 
> struts) when in the last line it calls upload.parseRequest which is in 
> FileUploadBase (part of commons-fileupload). That methods tries to find the 
> FileItems from the provided RequestContext, but considers every parameter 
> value to be a FileItem and then throws a new FileCountLimitExceededException 
> when the number of parameter values (of any type) reaches the fileCountMax.
> I am not sure if the problem is in struts or in fileupload. Maybe the 
> provided RequestContext should somehow only include the file parameters so 
> that only they get counted. Or perhaps the documentation should be changed to 
> clarify that struts.multipart.maxFiles specifies the maximum number of 
> parameter values a multipart form may include.
> I also found the issue 
> [FILEUPLOAD-351|https://issues.apache.org/jira/browse/FILEUPLOAD-351] that 
> seems to point out the confusing behaviour. But until the issue has been 
> resolved in fileupload, struts behaves contrary to its own documentation at 
> [https://struts.apache.org/core-developers/file-upload]
>  



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to