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

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

                Author: ASF GitHub Bot
            Created on: 27/Jan/24 08:22
            Start Date: 27/Jan/24 08:22
    Worklog Time Spent: 10m 
      Work Description: github-advanced-security[bot] commented on code in PR 
#861:
URL: https://github.com/apache/struts/pull/861#discussion_r1468415429


##########
core/src/main/java/org/apache/struts2/dispatcher/multipart/JakartaStreamMultiPartRequest.java:
##########
@@ -52,324 +48,119 @@
  *
  * @since 2.3.18
  */
-public class JakartaStreamMultiPartRequest extends AbstractMultiPartRequest {
+public class JakartaStreamMultiPartRequest extends 
AbstractMultiPartRequest<File> {
 
-    static final Logger LOG = 
LogManager.getLogger(JakartaStreamMultiPartRequest.class);
-
-    /**
-     * Map between file fields and file data.
-     */
-    protected Map<String, List<FileInfo>> fileInfos = new HashMap<>();
-
-    /**
-     * Map between non-file fields and values.
-     */
-    protected Map<String, List<String>> parameters = new HashMap<>();
-
-    /* (non-Javadoc)
-     * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#cleanUp()
-     */
-    public void cleanUp() {
-        LOG.debug("Performing File Upload temporary storage cleanup.");
-        for (List<FileInfo> fileInfoList : fileInfos.values()) {
-            for (FileInfo fileInfo : fileInfoList) {
-                File file = fileInfo.getFile();
-                LOG.debug("Deleting file '{}'.", file.getName());
-                if (!file.delete()) {
-                    LOG.warn("There was a problem attempting to delete file 
'{}'.", file.getName());
-                }
-            }
-        }
-    }
-
-    /* (non-Javadoc)
-     * @see 
org.apache.struts2.dispatcher.multipart.MultiPartRequest#getContentType(java.lang.String)
-     */
-    public String[] getContentType(String fieldName) {
-        List<FileInfo> infos = fileInfos.get(fieldName);
-        if (infos == null) {
-            return null;
-        }
-
-        List<String> types = new ArrayList<>(infos.size());
-        for (FileInfo fileInfo : infos) {
-            types.add(fileInfo.getContentType());
-        }
-
-        return types.toArray(new String[0]);
-    }
-
-    /* (non-Javadoc)
-     * @see 
org.apache.struts2.dispatcher.multipart.MultiPartRequest#getFile(java.lang.String)
-     */
-    public UploadedFile[] getFile(String fieldName) {
-        List<FileInfo> infos = fileInfos.get(fieldName);
-        if (infos == null) {
-            return null;
-        }
-
-        return infos.stream().map(fileInfo ->
-            StrutsUploadedFile.Builder.create(fileInfo.getFile())
-                .withContentType(fileInfo.contentType)
-                .withOriginalName(fileInfo.originalName)
-                .build()
-        ).toArray(UploadedFile[]::new);
-    }
-
-    /* (non-Javadoc)
-     * @see 
org.apache.struts2.dispatcher.multipart.MultiPartRequest#getFileNames(java.lang.String)
-     */
-    public String[] getFileNames(String fieldName) {
-        List<FileInfo> infos = fileInfos.get(fieldName);
-        if (infos == null) {
-            return null;
-        }
-
-        List<String> names = new ArrayList<>(infos.size());
-        for (FileInfo fileInfo : infos) {
-            names.add(getCanonicalName(fileInfo.getOriginalName()));
-        }
-
-        return names.toArray(new String[0]);
-    }
-
-    /* (non-Javadoc)
-     * @see 
org.apache.struts2.dispatcher.multipart.MultiPartRequest#getFileParameterNames()
-     */
-    public Enumeration<String> getFileParameterNames() {
-        return Collections.enumeration(fileInfos.keySet());
-    }
-
-    /* (non-Javadoc)
-     * @see 
org.apache.struts2.dispatcher.multipart.MultiPartRequest#getFilesystemName(java.lang.String)
-     */
-    public String[] getFilesystemName(String fieldName) {
-        List<FileInfo> infos = fileInfos.get(fieldName);
-        if (infos == null) {
-            return null;
-        }
-
-        List<String> names = new ArrayList<>(infos.size());
-        for (FileInfo fileInfo : infos) {
-            names.add(fileInfo.getFile().getName());
-        }
-
-        return names.toArray(new String[0]);
-    }
-
-    /* (non-Javadoc)
-     * @see 
org.apache.struts2.dispatcher.multipart.MultiPartRequest#getParameter(java.lang.String)
-     */
-    public String getParameter(String name) {
-        List<String> values = parameters.get(name);
-        if (values != null && !values.isEmpty()) {
-            return values.get(0);
-        }
-        return null;
-    }
-
-    /* (non-Javadoc)
-     * @see 
org.apache.struts2.dispatcher.multipart.MultiPartRequest#getParameterNames()
-     */
-    public Enumeration<String> getParameterNames() {
-        return Collections.enumeration(parameters.keySet());
-    }
-
-    /* (non-Javadoc)
-     * @see 
org.apache.struts2.dispatcher.multipart.MultiPartRequest#getParameterValues(java.lang.String)
-     */
-    public String[] getParameterValues(String name) {
-        List<String> values = parameters.get(name);
-        if (values != null && !values.isEmpty()) {
-            return values.toArray(new String[0]);
-        }
-        return null;
-    }
-
-    /* (non-Javadoc)
-     * @see 
org.apache.struts2.dispatcher.multipart.MultiPartRequest#parse(jakarta.servlet.http.HttpServletRequest,
 java.lang.String)
-     */
-    public void parse(HttpServletRequest request, String saveDir) throws 
IOException {
-        try {
-            setLocale(request);
-            processUpload(request, saveDir);
-        } catch (Exception e) {
-            LOG.debug("Error occurred during parsing of multi part request", 
e);
-            LocalizedMessage errorMessage = buildErrorMessage(e, new 
Object[]{});
-            if (!errors.contains(errorMessage)) {
-                errors.add(errorMessage);
-            }
-        }
-    }
+    private static final Logger LOG = 
LogManager.getLogger(JakartaStreamMultiPartRequest.class);
 
     /**
      * Processes the upload.
      *
      * @param request the servlet request
      * @param saveDir location of the save dir
      */
-    protected void processUpload(HttpServletRequest request, String saveDir) 
throws Exception {
-
+    @Override
+    protected void processUpload(HttpServletRequest request, String saveDir) 
throws IOException {
         // Sanity check that the request is a multi-part/form-data request.
-        if (JakartaServletFileUpload.isMultipartContent(request)) {
+        if (!JakartaServletFileUpload.isMultipartContent(request)) {
+            LOG.debug("Http request isn't: {}, stop processing", 
AbstractFileUpload.MULTIPART_FORM_DATA);
+            return;
+        }
 
-            // Sanity check on request size.
-            boolean requestSizePermitted = isRequestSizePermitted(request);
+        JakartaServletFileUpload<DiskFileItem, DiskFileItemFactory> 
servletFileUpload =
+                prepareServletFileUpload(request.getCharacterEncoding(), 
saveDir);
 
-            // Interface with Commons FileUpload API
-            // Using the Streaming API
-            JakartaServletFileUpload<DiskFileItem, DiskFileItemFactory> 
servletFileUpload = new JakartaServletFileUpload<>();
-            if (maxSize != null) {
-                servletFileUpload.setSizeMax(maxSize);
-            }
-            if (maxFiles != null) {
-                servletFileUpload.setFileCountMax(maxFiles);
-            }
-            if (maxFileSize != null) {
-                servletFileUpload.setFileSizeMax(maxFileSize);
+        LOG.debug("Using Jakarta Stream API to process request");
+        servletFileUpload.getItemIterator(request).forEachRemaining(item -> {
+            if (item.isFormField()) {
+                LOG.debug(() -> "Processing a form field: " + 
sanitizeNewlines(item.getFieldName()));
+                processFileItemAsFormField(item);
+            } else {
+                LOG.debug(() -> "Processing a file: " + 
sanitizeNewlines(item.getFieldName()));
+                processFileItemAsFileField(item, saveDir);
             }
-            FileItemInputIterator i = 
servletFileUpload.getItemIterator(request);
+        });
+    }
 
-            // Iterate the file items
-            while (i.hasNext()) {
-                try {
-                    FileItemInput itemStream = i.next();
+    protected JakartaServletDiskFileUpload createJakartaFileUpload(String 
charset, String saveDir) {
+        DiskFileItemFactory.Builder builder = DiskFileItemFactory.builder();
+        if (saveDir != null) {
+            LOG.debug("Using file save directory: {}", saveDir);
+            builder.setPath(saveDir);
+        }
 
-                    // If the file item stream is a form field, delegate to the
-                    // field item stream handler
-                    if (itemStream.isFormField()) {
-                        processFileItemStreamAsFormField(itemStream);
-                    }
+        LOG.debug("Sets buffer size: {}", bufferSize);
+        builder.setBufferSize(bufferSize);
 
-                    // Delegate the file item stream for a file field to the
-                    // file item stream handler, but delegation is skipped
-                    // if the requestSizePermitted check failed based on the
-                    // complete content-size of the request.
-                    else {
+        LOG.debug("Using charset: {} or default: {}", charset, 
defaultEncoding);

Review Comment:
   ## Logging should not be vulnerable to injection attacks
   
   <!--SONAR_ISSUE_KEY:AY1KAquc8H-R3IcxNTE7-->Change this code to not log 
user-controlled data. <p>See more on <a 
href="https://sonarcloud.io/project/issues?id=apache_struts&issues=AY1KAquc8H-R3IcxNTE7&open=AY1KAquc8H-R3IcxNTE7&pullRequest=861";>SonarCloud</a></p>
   
   [Show more 
details](https://github.com/apache/struts/security/code-scanning/425)



##########
core/src/main/java/org/apache/struts2/dispatcher/multipart/JakartaMultiPartRequest.java:
##########
@@ -19,377 +19,121 @@
 package org.apache.struts2.dispatcher.multipart;
 
 import jakarta.servlet.http.HttpServletRequest;
+import org.apache.commons.fileupload2.core.AbstractFileUpload;
 import org.apache.commons.fileupload2.core.DiskFileItem;
 import org.apache.commons.fileupload2.core.DiskFileItemFactory;
-import org.apache.commons.fileupload2.core.FileItem;
-import org.apache.commons.fileupload2.core.FileUploadByteCountLimitException;
-import org.apache.commons.fileupload2.core.FileUploadContentTypeException;
-import org.apache.commons.fileupload2.core.FileUploadException;
-import org.apache.commons.fileupload2.core.FileUploadFileCountLimitException;
-import org.apache.commons.fileupload2.core.FileUploadSizeException;
-import org.apache.commons.fileupload2.core.RequestContext;
-import org.apache.commons.fileupload2.jakarta.JakartaServletFileUpload;
+import 
org.apache.commons.fileupload2.jakarta.servlet6.JakartaServletDiskFileUpload;
+import 
org.apache.commons.fileupload2.jakarta.servlet6.JakartaServletFileUpload;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
-import org.apache.struts2.dispatcher.LocalizedMessage;
 
 import java.io.File;
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.UncheckedIOException;
 import java.nio.charset.Charset;
 import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
-import java.util.Set;
 
 /**
- * Multipart form data request adapter for Jakarta Commons Fileupload package.
+ * Multipart form data request adapter for Jakarta Commons FileUpload package.
  */
-public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
+public class JakartaMultiPartRequest extends AbstractMultiPartRequest<File> {
 
-    static final Logger LOG = 
LogManager.getLogger(JakartaMultiPartRequest.class);
+    private static final Logger LOG = 
LogManager.getLogger(JakartaMultiPartRequest.class);
 
-    // maps parameter name -> List of FileItem objects
-    protected Map<String, List<FileItem>> files = new HashMap<>();
+    @Override
+    protected void processUpload(HttpServletRequest request, String saveDir) 
throws IOException {
+        if (!JakartaServletFileUpload.isMultipartContent(request)) {
+            LOG.debug("Http request isn't: {}, stop processing", 
AbstractFileUpload.MULTIPART_FORM_DATA);
+            return;
+        }
 
-    // maps parameter name -> List of param values
-    protected Map<String, List<String>> params = new HashMap<>();
+        JakartaServletFileUpload<DiskFileItem, DiskFileItemFactory> upload =
+                prepareServletFileUpload(request.getCharacterEncoding(), 
saveDir);
 
-    /**
-     * Creates a new request wrapper to handle multipart data using methods 
adapted from Jason Pell's
-     * multipart classes (see class description).
-     *
-     * @param saveDir the directory to save off the file
-     * @param request the request containing the multipart
-     * @throws java.io.IOException is thrown if encoding fails.
-     */
-    public void parse(HttpServletRequest request, String saveDir) throws 
IOException {
-        try {
-            setLocale(request);
-            processUpload(request, saveDir);
-        } catch (FileUploadException e) {
-            LOG.debug("Request exceeded size limit!", e);
-            LocalizedMessage errorMessage;
-            if (e instanceof FileUploadByteCountLimitException) {
-                FileUploadByteCountLimitException ex = 
(FileUploadByteCountLimitException) e;
-                errorMessage = buildErrorMessage(e, new Object[]{
-                        ex.getFieldName(), ex.getFileName(), 
ex.getPermitted(), ex.getActualSize()
-                });
-            } else if (e instanceof FileUploadFileCountLimitException) {
-                FileUploadFileCountLimitException ex = 
(FileUploadFileCountLimitException) e;
-                errorMessage = buildErrorMessage(e, new Object[]{
-                        ex.getPermitted(), ex.getActualSize()
-                });
-            } else if (e instanceof FileUploadSizeException) {
-                FileUploadSizeException ex = (FileUploadSizeException) e;
-                errorMessage = buildErrorMessage(e, new Object[]{
-                        ex.getPermitted(), ex.getActualSize()
-                });
-            } else if (e instanceof FileUploadContentTypeException) {
-                FileUploadContentTypeException ex = 
(FileUploadContentTypeException) e;
-                errorMessage = buildErrorMessage(e, new Object[]{
-                        ex.getContentType()
-                });
+        for (DiskFileItem item : upload.parseRequest(request)) {
+            LOG.debug(() -> "Processing a form field: " + 
sanitizeNewlines(item.getFieldName()));
+            if (item.isFormField()) {
+                processNormalFormField(item, request.getCharacterEncoding());
             } else {
-                errorMessage = buildErrorMessage(e, new Object[]{});
-            }
-
-            if (!errors.contains(errorMessage)) {
-                errors.add(errorMessage);
-            }
-        } catch (Exception e) {
-            LOG.debug("Unable to parse request", e);
-            LocalizedMessage errorMessage = buildErrorMessage(e, new 
Object[]{});
-            if (!errors.contains(errorMessage)) {
-                errors.add(errorMessage);
+                LOG.debug(() -> "Processing a file: " + 
sanitizeNewlines(item.getFieldName()));
+                processFileField(item);
             }
         }
     }
 
-    protected void processUpload(HttpServletRequest request, String saveDir) 
throws IOException {
-
-        if (JakartaServletFileUpload.isMultipartContent(request)) {
-            for (FileItem item : parseRequest(request, saveDir)) {
-                LOG.debug("Found file item: [{}]", 
sanitizeNewlines(item.getFieldName()));
-                if (item.isFormField()) {
-                    processNormalFormField(item, 
request.getCharacterEncoding());
-                } else {
-                    processFileField(item);
-                }
-            }
+    protected JakartaServletDiskFileUpload createJakartaFileUpload(String 
charset, String saveDir) {
+        DiskFileItemFactory.Builder builder = DiskFileItemFactory.builder();
+        if (saveDir != null) {
+            LOG.debug("Using file save directory: {}", saveDir);
+            builder.setPath(saveDir);
         }
-    }
 
-    protected void processFileField(FileItem item) {
-        LOG.debug("Item is a file upload");
+        LOG.debug("Sets minimal buffer size to always write file to disk");
+        builder.setBufferSize(1);
 
-        // Skip file uploads that don't have a file name - meaning that no 
file was selected.
-        if (item.getName() == null || item.getName().trim().isEmpty()) {
-            LOG.debug("No file has been uploaded for the field: {}", 
sanitizeNewlines(item.getFieldName()));
-            return;
-        }
+        LOG.debug("Using charset: {} or default: {}", charset, 
defaultEncoding);

Review Comment:
   ## Logging should not be vulnerable to injection attacks
   
   <!--SONAR_ISSUE_KEY:AY1KAqt48H-R3IcxNTE6-->Change this code to not log 
user-controlled data. <p>See more on <a 
href="https://sonarcloud.io/project/issues?id=apache_struts&issues=AY1KAqt48H-R3IcxNTE6&open=AY1KAqt48H-R3IcxNTE6&pullRequest=861";>SonarCloud</a></p>
   
   [Show more 
details](https://github.com/apache/struts/security/code-scanning/424)





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

    Worklog Id:     (was: 902080)
    Time Spent: 2h 10m  (was: 2h)

> Upgrade Commons Fileupload to FileUpload Jakarta Servlet 6
> ----------------------------------------------------------
>
>                 Key: WW-5388
>                 URL: https://issues.apache.org/jira/browse/WW-5388
>             Project: Struts 2
>          Issue Type: Improvement
>          Components: Core
>            Reporter: Lukasz Lenart
>            Assignee: Lukasz Lenart
>            Priority: Major
>             Fix For: 7.0.0
>
>          Time Spent: 2h 10m
>  Remaining Estimate: 0h
>
> There is a new version of JakartaEE FileUpload
> {code:xml}
> <dependency>
>   <groupId>org.apache.commons</groupId>
>   <artifactId>commons-fileupload2-jakarta-servlet6</artifactId>
>   <version>2.0.0-M2</version>
> </dependency>
> {code}



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

Reply via email to