You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by tn...@apache.org on 2013/03/12 23:01:02 UTC

svn commit: r1455729 - in /commons/proper/fileupload/trunk/src: main/java/org/apache/commons/fileupload/ test/java/org/apache/commons/fileupload/

Author: tn
Date: Tue Mar 12 22:01:02 2013
New Revision: 1455729

URL: http://svn.apache.org/r1455729
Log:
[FILEUPLOAD-212] Add more unit tests for sizeMax setting; propagate correctly any SizeException to the caller.

Modified:
    commons/proper/fileupload/trunk/src/main/java/org/apache/commons/fileupload/FileUploadBase.java
    commons/proper/fileupload/trunk/src/main/java/org/apache/commons/fileupload/MultipartStream.java
    commons/proper/fileupload/trunk/src/test/java/org/apache/commons/fileupload/MockHttpServletRequest.java
    commons/proper/fileupload/trunk/src/test/java/org/apache/commons/fileupload/SizesTest.java

Modified: commons/proper/fileupload/trunk/src/main/java/org/apache/commons/fileupload/FileUploadBase.java
URL: http://svn.apache.org/viewvc/commons/proper/fileupload/trunk/src/main/java/org/apache/commons/fileupload/FileUploadBase.java?rev=1455729&r1=1455728&r2=1455729&view=diff
==============================================================================
--- commons/proper/fileupload/trunk/src/main/java/org/apache/commons/fileupload/FileUploadBase.java (original)
+++ commons/proper/fileupload/trunk/src/main/java/org/apache/commons/fileupload/FileUploadBase.java Tue Mar 12 22:01:02 2013
@@ -305,7 +305,12 @@ public abstract class FileUploadBase {
      */
     public FileItemIterator getItemIterator(RequestContext ctx)
     throws FileUploadException, IOException {
-        return new FileItemIteratorImpl(ctx);
+        try {
+            return new FileItemIteratorImpl(ctx);
+        } catch (FileUploadIOException e) {
+            // unwrap encapsulated SizeException
+            throw (FileUploadException) e.getCause();
+        }
     }
 
     /**
@@ -328,20 +333,17 @@ public abstract class FileUploadBase {
             FileItemIterator iter = getItemIterator(ctx);
             FileItemFactory fac = getFileItemFactory();
             if (fac == null) {
-                throw new NullPointerException(
-                    "No FileItemFactory has been set.");
+                throw new NullPointerException("No FileItemFactory has been set.");
             }
             while (iter.hasNext()) {
                 final FileItemStream item = iter.next();
                 // Don't use getName() here to prevent an InvalidFileNameException.
                 final String fileName = ((FileItemIteratorImpl.FileItemStreamImpl) item).name;
-                FileItem fileItem = fac.createItem(item.getFieldName(),
-                        item.getContentType(), item.isFormField(),
-                        fileName);
+                FileItem fileItem = fac.createItem(item.getFieldName(), item.getContentType(),
+                                                   item.isFormField(), fileName);
                 items.add(fileItem);
                 try {
-                    Streams.copy(item.openStream(), fileItem.getOutputStream(),
-                            true);
+                    Streams.copy(item.openStream(), fileItem.getOutputStream(), true);
                 } catch (FileUploadIOException e) {
                     throw (FileUploadException) e.getCause();
                 } catch (IOException e) {
@@ -1091,7 +1093,12 @@ public abstract class FileUploadBase {
             if (itemValid) {
                 return true;
             }
-            return findNextItem();
+            try {
+                return findNextItem();
+            } catch (FileUploadIOException e) {
+                // unwrap encapsulated SizeException
+                throw (FileUploadException) e.getCause();
+            }
         }
 
         /**

Modified: commons/proper/fileupload/trunk/src/main/java/org/apache/commons/fileupload/MultipartStream.java
URL: http://svn.apache.org/viewvc/commons/proper/fileupload/trunk/src/main/java/org/apache/commons/fileupload/MultipartStream.java?rev=1455729&r1=1455728&r2=1455729&view=diff
==============================================================================
--- commons/proper/fileupload/trunk/src/main/java/org/apache/commons/fileupload/MultipartStream.java (original)
+++ commons/proper/fileupload/trunk/src/main/java/org/apache/commons/fileupload/MultipartStream.java Tue Mar 12 22:01:02 2013
@@ -24,6 +24,7 @@ import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.UnsupportedEncodingException;
 
+import org.apache.commons.fileupload.FileUploadBase.FileUploadIOException;
 import org.apache.commons.fileupload.util.Closeable;
 import org.apache.commons.fileupload.util.Streams;
 
@@ -438,7 +439,7 @@ public class MultipartStream {
      *                                  fails to follow required syntax.
      */
     public boolean readBoundary()
-            throws MalformedStreamException {
+            throws FileUploadIOException, MalformedStreamException {
         byte[] marker = new byte[2];
         boolean nextChunk = false;
 
@@ -464,6 +465,9 @@ public class MultipartStream {
                 throw new MalformedStreamException(
                 "Unexpected characters follow a boundary");
             }
+        } catch (FileUploadIOException e) {
+            // wraps a SizeException, re-throw as it will be unwrapped later
+            throw e;
         } catch (IOException e) {
             throw new MalformedStreamException("Stream ended unexpectedly");
         }
@@ -514,7 +518,7 @@ public class MultipartStream {
      *
      * @throws MalformedStreamException if the stream ends unexpecetedly.
      */
-    public String readHeaders() throws MalformedStreamException {
+    public String readHeaders() throws FileUploadIOException, MalformedStreamException {
         int i = 0;
         byte b;
         // to support multi-byte characters
@@ -523,6 +527,9 @@ public class MultipartStream {
         while (i < HEADER_SEPARATOR.length) {
             try {
                 b = readByte();
+            } catch (FileUploadIOException e) {
+                // wraps a SizeException, re-throw as it will be unwrapped later
+                throw e;
             } catch (IOException e) {
                 throw new MalformedStreamException("Stream ended unexpectedly");
             }

Modified: commons/proper/fileupload/trunk/src/test/java/org/apache/commons/fileupload/MockHttpServletRequest.java
URL: http://svn.apache.org/viewvc/commons/proper/fileupload/trunk/src/test/java/org/apache/commons/fileupload/MockHttpServletRequest.java?rev=1455729&r1=1455728&r2=1455729&view=diff
==============================================================================
--- commons/proper/fileupload/trunk/src/test/java/org/apache/commons/fileupload/MockHttpServletRequest.java (original)
+++ commons/proper/fileupload/trunk/src/test/java/org/apache/commons/fileupload/MockHttpServletRequest.java Tue Mar 12 22:01:02 2013
@@ -39,9 +39,11 @@ class MockHttpServletRequest implements 
 
     private final InputStream m_requestData;
 
-    private final long length;
+    private long length;
 
     private String m_strContentType;
+    
+    private int readLimit = -1;
 
     private final Map<String, String> m_headers = new java.util.HashMap<String, String>();
 
@@ -295,6 +297,13 @@ class MockHttpServletRequest implements 
     }
 
     /**
+     * For testing attack scenarios in SizesTest.
+     */
+    public void setContentLength(long length) {
+        this.length = length;
+    }
+    
+    /**
      * @see javax.servlet.ServletRequest#getContentType()
      */
     public String getContentType() {
@@ -305,11 +314,20 @@ class MockHttpServletRequest implements 
      * @see javax.servlet.ServletRequest#getInputStream()
      */
     public ServletInputStream getInputStream() throws IOException {
-        ServletInputStream sis = new MyServletInputStream(m_requestData);
+        ServletInputStream sis = new MyServletInputStream(m_requestData, readLimit);
         return sis;
     }
 
     /**
+     * Sets the read limit. This can be used to limit the number of bytes to read ahead.
+     *
+     * @param readLimit the read limit to use
+     */
+    public void setReadLimit(int readLimit) {
+        this.readLimit = readLimit;
+    }
+
+    /**
      * @see javax.servlet.ServletRequest#getParameter(String)
      */
     public String getParameter(String arg0) {
@@ -463,23 +481,19 @@ class MockHttpServletRequest implements 
         return null;
     }
 
-    /**
-     *
-     *
-     *
-     *
-     */
     private static class MyServletInputStream
         extends javax.servlet.ServletInputStream {
 
         private final InputStream in;
+        private final int readLimit;
 
         /**
          * Creates a new instance, which returns the given
          * streams data.
          */
-        public MyServletInputStream(InputStream pStream) {
+        public MyServletInputStream(InputStream pStream, int readLimit) {
             in = pStream;
+            this.readLimit = readLimit;
         }
 
         @Override
@@ -489,7 +503,11 @@ class MockHttpServletRequest implements 
 
         @Override
         public int read(byte b[], int off, int len) throws IOException {
-            return in.read(b, off, len);
+            if (readLimit > 0) {
+                return in.read(b, off, Math.min(readLimit, len));
+            } else {
+                return in.read(b, off, len);
+            }
         }
 
     }

Modified: commons/proper/fileupload/trunk/src/test/java/org/apache/commons/fileupload/SizesTest.java
URL: http://svn.apache.org/viewvc/commons/proper/fileupload/trunk/src/test/java/org/apache/commons/fileupload/SizesTest.java?rev=1455729&r1=1455728&r2=1455729&view=diff
==============================================================================
--- commons/proper/fileupload/trunk/src/test/java/org/apache/commons/fileupload/SizesTest.java (original)
+++ commons/proper/fileupload/trunk/src/test/java/org/apache/commons/fileupload/SizesTest.java Tue Mar 12 22:01:02 2013
@@ -17,18 +17,23 @@
 package org.apache.commons.fileupload;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.util.Iterator;
 import java.util.List;
 
 import javax.servlet.http.HttpServletRequest;
 
+import org.apache.commons.fileupload.FileUploadBase.FileUploadIOException;
+import org.apache.commons.fileupload.FileUploadBase.SizeException;
 import org.apache.commons.fileupload.disk.DiskFileItemFactory;
 import org.apache.commons.fileupload.servlet.ServletFileUpload;
+import org.apache.commons.fileupload.util.Streams;
 import org.junit.Test;
 
 /**
@@ -122,4 +127,161 @@ public class SizesTest extends FileUploa
         }
     }
 
+    /** Checks, whether a faked Content-Length header is detected.
+     */
+    @Test
+    public void testFileSizeLimitWithFakedContentLength()
+            throws IOException, FileUploadException {
+        final String request =
+            "-----1234\r\n" +
+            "Content-Disposition: form-data; name=\"file\"; filename=\"foo.tab\"\r\n" +
+            "Content-Type: text/whatever\r\n" +
+            "Content-Length: 10\r\n" +
+            "\r\n" +
+            "This is the content of the file\n" +
+            "\r\n" +
+            "-----1234--\r\n";
+
+        ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory());
+        upload.setFileSizeMax(-1);
+        HttpServletRequest req = new MockHttpServletRequest(request.getBytes("US-ASCII"), CONTENT_TYPE);
+        List<FileItem> fileItems = upload.parseRequest(req);
+        assertEquals(1, fileItems.size());
+        FileItem item = fileItems.get(0);
+        assertEquals("This is the content of the file\n", new String(item.get()));
+
+        upload = new ServletFileUpload(new DiskFileItemFactory());
+        upload.setFileSizeMax(40);
+        req = new MockHttpServletRequest(request.getBytes("US-ASCII"), CONTENT_TYPE);
+        fileItems = upload.parseRequest(req);
+        assertEquals(1, fileItems.size());
+        item = fileItems.get(0);
+        assertEquals("This is the content of the file\n", new String(item.get()));
+
+        // provided Content-Length is larger than the FileSizeMax -> handled by ctor
+        upload = new ServletFileUpload(new DiskFileItemFactory());
+        upload.setFileSizeMax(5);
+        req = new MockHttpServletRequest(request.getBytes("US-ASCII"), CONTENT_TYPE);
+        try {
+            upload.parseRequest(req);
+            fail("Expected exception.");
+        } catch (FileUploadBase.FileSizeLimitExceededException e) {
+            assertEquals(5, e.getPermittedSize());
+        }
+        
+        // provided Content-Length is wrong, actual content is larger -> handled by LimitedInputStream
+        upload = new ServletFileUpload(new DiskFileItemFactory());
+        upload.setFileSizeMax(15);
+        req = new MockHttpServletRequest(request.getBytes("US-ASCII"), CONTENT_TYPE);
+        try {
+            upload.parseRequest(req);
+            fail("Expected exception.");
+        } catch (FileUploadBase.FileSizeLimitExceededException e) {
+            assertEquals(15, e.getPermittedSize());
+        }
+    }
+
+    /** Checks, whether the maxSize works.
+     */
+    @Test
+    public void testMaxSizeLimit()
+            throws IOException, FileUploadException {
+        final String request =
+            "-----1234\r\n" +
+            "Content-Disposition: form-data; name=\"file1\"; filename=\"foo1.tab\"\r\n" +
+            "Content-Type: text/whatever\r\n" +
+            "Content-Length: 10\r\n" +
+            "\r\n" +
+            "This is the content of the file\n" +
+            "\r\n" +
+            "-----1234\r\n" +
+            "Content-Disposition: form-data; name=\"file2\"; filename=\"foo2.tab\"\r\n" +
+            "Content-Type: text/whatever\r\n" +
+            "\r\n" +
+            "This is the content of the file\n" +
+            "\r\n" +
+            "-----1234--\r\n";
+
+        ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory());
+        upload.setFileSizeMax(-1);
+        upload.setSizeMax(200);
+        
+        MockHttpServletRequest req = new MockHttpServletRequest(request.getBytes("US-ASCII"), CONTENT_TYPE);
+        try {
+            upload.parseRequest(req);
+            fail("Expected exception.");
+        } catch (FileUploadBase.SizeLimitExceededException e) {
+            assertEquals(200, e.getPermittedSize());
+        }
+
+    }
+
+    @Test
+    public void testMaxSizeLimitUnknownContentLength()
+            throws IOException, FileUploadException {
+        final String request =
+            "-----1234\r\n" +
+            "Content-Disposition: form-data; name=\"file1\"; filename=\"foo1.tab\"\r\n" +
+            "Content-Type: text/whatever\r\n" +
+            "Content-Length: 10\r\n" +
+            "\r\n" +
+            "This is the content of the file\n" + 
+            "\r\n" +
+            "-----1234\r\n" +
+            "Content-Disposition: form-data; name=\"file2\"; filename=\"foo2.tab\"\r\n" +
+            "Content-Type: text/whatever\r\n" +
+            "\r\n" +
+            "This is the content of the file\n" +
+            "\r\n" +
+            "-----1234--\r\n";
+
+        ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory());
+        upload.setFileSizeMax(-1);
+        upload.setSizeMax(300);
+
+        // the first item should be within the max size limit
+        // set the read limit to 10 to simulate a "real" stream
+        // otherwise the buffer would be immediately filled
+
+        MockHttpServletRequest req = new MockHttpServletRequest(request.getBytes("US-ASCII"), CONTENT_TYPE);
+        req.setContentLength(-1);
+        req.setReadLimit(10);
+        
+        FileItemIterator it = upload.getItemIterator(req);
+        assertTrue(it.hasNext());
+        
+        FileItemStream item = it.next();
+        assertFalse(item.isFormField());
+        assertEquals("file1", item.getFieldName());
+        assertEquals("foo1.tab", item.getName());
+        
+        try {
+            InputStream stream = item.openStream();
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            Streams.copy(stream, baos, true);
+        } catch (FileUploadIOException e) {
+            fail(e.getMessage());
+        }
+
+        // the second item is over the size max, thus we expect an error
+        try {
+            // the header is still within size max -> this shall still succeed
+            assertTrue(it.hasNext());
+        } catch (SizeException e) {
+            fail();
+        }
+
+        item = it.next();
+
+        try {
+            InputStream stream = item.openStream();
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            Streams.copy(stream, baos, true);
+            fail();
+        } catch (FileUploadIOException e) {
+            // expected
+        }
+
+    }
+
 }