This is an automated email from the ASF dual-hosted git repository. garydgregory pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-fileupload.git
commit 5cfa56ae76e445340e9b8c90504037d58c0eab22 Author: Gary Gregory <[email protected]> AuthorDate: Sat Jul 18 07:23:17 2026 -0700 Ignore invalid RFC 2231 extended value so it cannot erase a valid filename (#479). --- .../fileupload2/core/ParameterParserTest.java | 30 ++++++++++++---------- src/changes/changes.xml | 1 + 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/commons-fileupload2-core/src/test/java/org/apache/commons/fileupload2/core/ParameterParserTest.java b/commons-fileupload2-core/src/test/java/org/apache/commons/fileupload2/core/ParameterParserTest.java index 123a10a1..c14784a6 100644 --- a/commons-fileupload2-core/src/test/java/org/apache/commons/fileupload2/core/ParameterParserTest.java +++ b/commons-fileupload2-core/src/test/java/org/apache/commons/fileupload2/core/ParameterParserTest.java @@ -28,6 +28,10 @@ import org.junit.jupiter.api.Test; */ class ParameterParserTest { + private char[] newSeparatorArray() { + return new char[] { ',', ';' }; + } + @Test void testContentTypeParsing() { final var s = "text/plain; Charset=UTF-8"; @@ -42,7 +46,7 @@ class ParameterParserTest { void testFileUpload139() { final var parser = new ParameterParser(); var s = "Content-type: multipart/form-data , boundary=AaB03x"; - var params = parser.parse(s, new char[] { ',', ';' }); + var params = parser.parse(s, newSeparatorArray()); assertEquals("AaB03x", params.get("boundary")); s = "Content-type: multipart/form-data, boundary=AaB03x"; @@ -50,7 +54,7 @@ class ParameterParserTest { assertEquals("AaB03x", params.get("boundary")); s = "Content-type: multipart/mixed, boundary=BbC04y"; - params = parser.parse(s, new char[] { ',', ';' }); + params = parser.parse(s, newSeparatorArray()); assertEquals("BbC04y", params.get("boundary")); } @@ -62,7 +66,7 @@ class ParameterParserTest { final var parser = new ParameterParser(); final var s = "Content-Disposition: form-data; name=\"file\"; filename=\"=?ISO-8859-" + "1?B?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?= =?ISO-8859-2?B?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?=\"\r\n"; - final var params = parser.parse(s, new char[] { ',', ';' }); + final var params = parser.parse(s, newSeparatorArray()); assertEquals("If you can read this you understand the example.", params.get("filename")); } @@ -75,27 +79,27 @@ class ParameterParserTest { // Should parse a UTF-8 charset var s = "Content-Disposition: form-data; " + "name=\"file\"; filename*=UTF-8''%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF\r\n"; - var params = parser.parse(s, new char[] { ',', ';' }); + var params = parser.parse(s, newSeparatorArray()); assertEquals("\u3053\u3093\u306B\u3061\u306F", params.get("filename")); // filename = "こんにちは" in japanese // Should parse ISO-8859-1 charset s = "Content-Disposition: form-data; name=\"file\"; filename*=UTF-8''%70%C3%A2%74%C3%A9\r\n"; - params = parser.parse(s, new char[] { ',', ';' }); + params = parser.parse(s, newSeparatorArray()); assertEquals("\u0070\u00e2\u0074\u00e9", params.get("filename")); // filename = "pâté" in french // Should not decode if '*' is not at the end of param-name s = "Content-Disposition: form-data; name=\"file\"; file*name=UTF-8''%61%62%63\r\n"; - params = parser.parse(s, new char[] { ',', ';' }); + params = parser.parse(s, newSeparatorArray()); assertEquals("UTF-8''%61%62%63", params.get("file*name")); // Should not decode if param-value does not follow <charset>'<lang>'<encoded> s = "Content-Disposition: form-data; name=\"file\"; filename*=a'bc\r\n"; - params = parser.parse(s, new char[] { ',', ';' }); + params = parser.parse(s, newSeparatorArray()); assertEquals("a'bc", params.get("filename")); // Should not decode if param-name doesn't have '*' at end s = "Content-Disposition: form-data; name=\"file\"; filename=a'b'c\r\n"; - params = parser.parse(s, new char[] { ',', ';' }); + params = parser.parse(s, newSeparatorArray()); assertEquals("a'b'c", params.get("filename")); } @@ -108,22 +112,22 @@ class ParameterParserTest { // A truncated %nn escape in filename* must not erase the valid filename. var s = "Content-Disposition: form-data; name=\"file\"; filename=\"safe.txt\"; filename*=UTF-8''bad%2\r\n"; - var params = parser.parse(s, new char[] { ',', ';' }); + var params = parser.parse(s, newSeparatorArray()); assertEquals("safe.txt", params.get("filename")); // Order independent: an invalid filename* before the valid filename is also ignored. s = "Content-Disposition: form-data; name=\"file\"; filename*=UTF-8''bad%2; filename=\"safe.txt\"\r\n"; - params = parser.parse(s, new char[] { ',', ';' }); + params = parser.parse(s, newSeparatorArray()); assertEquals("safe.txt", params.get("filename")); // An invalid filename* on its own is not reported as a (null) value. s = "Content-Disposition: form-data; name=\"file\"; filename*=UTF-8''bad%2\r\n"; - params = parser.parse(s, new char[] { ',', ';' }); + params = parser.parse(s, newSeparatorArray()); assertNull(params.get("filename")); // A valid filename* still decodes normally. s = "Content-Disposition: form-data; name=\"file\"; filename=\"safe.txt\"; filename*=UTF-8''real.exe\r\n"; - params = parser.parse(s, new char[] { ',', ';' }); + params = parser.parse(s, newSeparatorArray()); assertEquals("real.exe", params.get("filename")); } @@ -137,7 +141,7 @@ class ParameterParserTest { assertEquals("stuff; stuff", params.get("test2")); assertEquals("\"stuff", params.get("test3")); - params = parser.parse(s, new char[] { ',', ';' }); + params = parser.parse(s, newSeparatorArray()); assertNull(params.get("test")); assertEquals("stuff", params.get("test1")); assertEquals("stuff; stuff", params.get("test2")); diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 8a90e196..ed3f2edc 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -50,6 +50,7 @@ The <action> type attribute can be add,update,fix,remove. <action type="fix" dev="ggregory" due-to="alhuda, Gary Gregory">Create temporary upload file with owner-only permissions (#473).</action> <action type="fix" dev="ggregory" due-to="alhuda, Gary Gregory">Match RFC 2047 encoding token case-insensitively in decodeWord (#476).</action> <action type="fix" dev="ggregory" due-to="alhuda, Gary Gregory">Reject malformed base64 payload in RFC 2047 decodeWord (#478).</action> + <action type="fix" dev="ggregory" due-to="alhuda, Gary Gregory">Ignore invalid RFC 2231 extended value so it cannot erase a valid filename (#479).</action> <!-- ADD --> <!-- UPDATE --> <action type="update" dev="ggregory" due-to="Gary Gregory">Bump org.apache.commons:commons-parent from 96 to 102.</action>
