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)



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to