You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by jo...@apache.org on 2025/06/08 14:23:49 UTC
(commons-fileupload) branch master updated: Adding support for the partHeaderSizeMax limit by cherry-picking 2108495a4775910b8559f18ed5a779d60542ee96
This is an automated email from the ASF dual-hosted git repository.
jochen pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-fileupload.git
The following commit(s) were added to refs/heads/master by this push:
new e5b5543b Adding support for the partHeaderSizeMax limit by cherry-picking 2108495a4775910b8559f18ed5a779d60542ee96
e5b5543b is described below
commit e5b5543b3a40ac9dde3c33ef1858901b3ca6656a
Author: Jochen Wiedmann <jo...@gmail.com>
AuthorDate: Sun Jun 8 16:20:20 2025 +0200
Adding support for the partHeaderSizeMax limit by cherry-picking 2108495a4775910b8559f18ed5a779d60542ee96
---
.../commons/fileupload2/core/MultipartInput.java | 62 ++++++++++++++++++----
.../fileupload2/core/MultipartStreamTest.java | 55 +++++++++++++++++++
src/changes/changes.xml | 1 +
3 files changed, 109 insertions(+), 9 deletions(-)
diff --git a/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/core/MultipartInput.java b/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/core/MultipartInput.java
index 68a63deb..b7ffc9e3 100644
--- a/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/core/MultipartInput.java
+++ b/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/core/MultipartInput.java
@@ -110,6 +110,10 @@ public final class MultipartInput {
*/
private ProgressNotifier progressNotifier;
+ /** The per part size limit for headers.
+ */
+ private int partHeaderSizeMax = DEFAULT_PART_HEADER_SIZE_MAX;
+
/**
* Constructs a new instance.
*/
@@ -134,7 +138,7 @@ public final class MultipartInput {
*/
@Override
public MultipartInput get() throws IOException {
- return new MultipartInput(getInputStream(), boundary, getBufferSize(), progressNotifier);
+ return new MultipartInput(getInputStream(), boundary, getBufferSize(), getPartHeaderSizeMax(), progressNotifier);
}
/**
@@ -148,6 +152,15 @@ public final class MultipartInput {
return this;
}
+ /** Sets the per part size limit for headers.
+ * @param partHeaderSizeMax The maximum size of the headers in bytes.
+ * @since 2.0.0-M4
+ */
+ public Builder setPartHeaderSizeMax(int partHeaderSizeMax) {
+ this.partHeaderSizeMax = partHeaderSizeMax;
+ return this;
+ }
+
/**
* Sets the progress notifier.
*
@@ -159,6 +172,14 @@ public final class MultipartInput {
return this;
}
+ /** Returns the per part size limit for headers.
+ * @return The maximum size of the headers in bytes.
+ * @since 2.0.0-M4
+ */
+ public int getPartHeaderSizeMax() {
+ return partHeaderSizeMax;
+ }
+
}
/**
@@ -543,14 +564,15 @@ public final class MultipartInput {
public static final byte DASH = 0x2D;
/**
- * The maximum length of {@code header-part} that will be processed (10 kilobytes = 10240 bytes.).
+ * The default length of the buffer used for processing a request.
*/
- public static final int HEADER_PART_SIZE_MAX = 10_240;
+ static final int DEFAULT_BUFSIZE = 4096;
/**
- * The default length of the buffer used for processing a request.
+ * Default per part header size limit in bytes.
+ * @since 2.0.0-M4
*/
- static final int DEFAULT_BUFSIZE = 4096;
+ public static final int DEFAULT_PART_HEADER_SIZE_MAX=512;
/**
* A byte sequence that marks the end of {@code header-part} ({@code CRLFCRLF}).
@@ -655,6 +677,14 @@ public final class MultipartInput {
*/
private final ProgressNotifier notifier;
+ /** The per part size limit for headers.
+ *
+ * @param partHeaderSizeMax The maximum size of the headers in bytes.
+ *Add commentMore actions
+ * @since 2.0.0-M4
+ */
+ private int partHeaderSizeMax;
+
/**
* Constructs a {@code MultipartInput} with a custom size buffer.
* <p>
@@ -668,7 +698,7 @@ public final class MultipartInput {
* @param notifier The notifier, which is used for calling the progress listener, if any.
* @throws IllegalArgumentException If the buffer size is too small.
*/
- private MultipartInput(final InputStream input, final byte[] boundary, final int bufferSize, final ProgressNotifier notifier) {
+ private MultipartInput(final InputStream input, final byte[] boundary, final int bufferSize, final int partHeaderSizeMax, final ProgressNotifier notifier) {
if (boundary == null) {
throw new IllegalArgumentException("boundary may not be null");
}
@@ -683,6 +713,7 @@ public final class MultipartInput {
this.bufSize = Math.max(bufferSize, boundaryLength * 2);
this.buffer = new byte[this.bufSize];
this.notifier = notifier;
+ this.partHeaderSizeMax = partHeaderSizeMax;
this.boundary = new byte[this.boundaryLength];
this.boundaryTable = new int[this.boundaryLength + 1];
@@ -782,6 +813,15 @@ public final class MultipartInput {
return headerCharset;
}
+ /** Returns the per part size limit for headers.
+ *
+ * @param partHeaderSizeMax The maximum size of the headers in bytes.
+ * @since 2.0.0-M4
+ */
+ public int getPartHeaderSizeMax() {
+ return partHeaderSizeMax;
+ }
+
/**
* Creates a new {@link ItemInputStream}.
*
@@ -875,6 +915,9 @@ public final class MultipartInput {
* <p>
* Headers are returned verbatim to the input stream, including the trailing {@code CRLF} marker. Parsing is left to the application.
* </p>
+ * <p>
+ * <strong>TODO</strong> allow limiting maximum header size to protect against abuse.
+ * </p>
*
* @return The {@code header-part} of the current encapsulation.
* @throws FileUploadSizeException if the bytes read from the stream exceeded the size limits.
@@ -895,9 +938,10 @@ public final class MultipartInput {
} catch (final IOException e) {
throw new MalformedStreamException("Stream ended unexpectedly", e);
}
- if (++size > HEADER_PART_SIZE_MAX) {
- throw new MalformedStreamException(
- String.format("Header section has more than %s bytes (maybe it is not properly terminated)", HEADER_PART_SIZE_MAX));
+ int phsm = getPartHeaderSizeMax();
+ if (phsm != -1 && ++size > phsm) {
+ throw new FileUploadSizeException(String.format("Header section has more than %s bytes (maybe it is not properly terminated)", Integer.valueOf(phsm)),
+ (long) phsm, (long) size);
}
if (b == HEADER_SEPARATOR[i]) {
i++;
diff --git a/commons-fileupload2-core/src/test/java/org/apache/commons/fileupload2/core/MultipartStreamTest.java b/commons-fileupload2-core/src/test/java/org/apache/commons/fileupload2/core/MultipartStreamTest.java
index 61d26afa..7a70ecec 100644
--- a/commons-fileupload2-core/src/test/java/org/apache/commons/fileupload2/core/MultipartStreamTest.java
+++ b/commons-fileupload2-core/src/test/java/org/apache/commons/fileupload2/core/MultipartStreamTest.java
@@ -16,11 +16,16 @@
*/
package org.apache.commons.fileupload2.core;
+import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.fail;
import java.io.ByteArrayInputStream;
+import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
import org.junit.jupiter.api.Test;
@@ -65,4 +70,54 @@ public class MultipartStreamTest {
assertNotNull(ms);
}
+ /** Checks, whether the maxSize works.
+ */
+ @Test
+ public void testPartHeaderSizeMaxLimit()
+ throws Exception {
+ 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";
+
+ final String strContents = request;
+ final byte[] byteContents = request.getBytes(StandardCharsets.UTF_8);
+ final InputStream input = new ByteArrayInputStream(byteContents);
+ final byte[] boundary = "---1234".getBytes();
+ final MultipartInput mi = MultipartInput.builder().setInputStream(input).setBoundary(boundary)
+ .setPartHeaderSizeMax(100).get();
+ assertNotNull(mi);
+ try {
+ boolean nextPart = mi.skipPreamble();
+ OutputStream nullOutput = new OutputStream() {
+ @Override
+ public void write(int pB) throws IOException {
+ // Do nothing. (Null output)
+ }
+ };
+ while (nextPart) {
+ String headers = mi.readHeaders();
+ System.out.print("Headers=" + headers.length() + ", " + headers);
+ assertNotNull(headers);
+ // process headers
+ // create some output stream
+ mi.readBodyData(nullOutput);
+ nextPart = mi.readBoundary();
+ }
+ fail("Expected Exception");
+ } catch (FileUploadSizeException fuse) {
+ assertEquals(100, fuse.getPermitted());
+ }
+ }
}
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index e6799568..895bfefa 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -45,6 +45,7 @@ The <action> type attribute can be add,update,fix,remove.
<!-- FIX -->
<action type="fix" dev="ppkarwasz" due-to="Basil Crow">Simplify exception handling in FileItem API #309.</action>
<!-- ADD -->
+ <action type="add" dev="jochen" due-to="Mark Thomas">Add partHeaderSizeMax, a new limit that sets a maximum number of bytes for each individual multipart header. The default is 512 bytes.</action>
<!-- UPDATE -->
</release>
<release version="2.0.0-M3" date="2025-05-07" description="This release requires Java 11.">