2626import java .io .IOException ;
2727import java .lang .reflect .Field ;
2828import java .nio .charset .StandardCharsets ;
29+ import java .nio .file .Files ;
30+ import java .nio .file .Path ;
2931import java .util .List ;
3032
3133import static org .apache .commons .lang3 .StringUtils .normalizeSpace ;
@@ -112,7 +114,7 @@ public void temporaryFileCreationFailureAddsError() throws IOException {
112114 // Create a custom implementation that simulates temp file creation failure
113115 class FaultyJakartaMultiPartRequest extends JakartaMultiPartRequest {
114116 @ Override
115- protected void processFileField (DiskFileItem item ) {
117+ protected void processFileField (DiskFileItem item , String saveDir ) {
116118 // Simulate in-memory upload that fails to create temp file
117119 if (item .isInMemory ()) {
118120 try {
@@ -127,7 +129,7 @@ protected void processFileField(DiskFileItem item) {
127129 }
128130 }
129131 } else {
130- super .processFileField (item );
132+ super .processFileField (item , saveDir );
131133 }
132134 }
133135 }
@@ -219,4 +221,64 @@ public void endToEndMultipartProcessingWithCleanup() throws IOException {
219221 assertThat (multiPart .parameters ).isEmpty ();
220222 }
221223
224+ @ Test
225+ public void temporaryFilesCreatedInSaveDirectory () throws IOException , NoSuchFieldException , IllegalAccessException {
226+ // Test that temporary files for in-memory uploads are created in the saveDir, not system temp
227+ String content = formFile ("file1" , "test1.csv" , "small,content" ) +
228+ endline + "--" + boundary + "--" ;
229+
230+ mockRequest .setContent (content .getBytes (StandardCharsets .UTF_8 ));
231+
232+ // when
233+ multiPart .parse (mockRequest , tempDir );
234+
235+ // Access private field to get temporary files
236+ Field tempFilesField = JakartaMultiPartRequest .class .getDeclaredField ("temporaryFiles" );
237+ tempFilesField .setAccessible (true );
238+ @ SuppressWarnings ("unchecked" )
239+ List <File > temporaryFiles = (List <File >) tempFilesField .get (multiPart );
240+
241+ // then - verify temporary files are created in saveDir
242+ assertThat (temporaryFiles ).isNotEmpty ();
243+ for (File tempFile : temporaryFiles ) {
244+ // Verify the temporary file is in the saveDir, not system temp
245+ assertThat (tempFile .getParent ()).isEqualTo (tempDir );
246+ assertThat (tempFile .getName ()).startsWith ("upload_" );
247+ assertThat (tempFile .getName ()).endsWith (".tmp" );
248+ assertThat (tempFile ).exists ();
249+ }
250+ }
251+
252+ @ Test
253+ public void secureTemporaryFileNaming () throws IOException , NoSuchFieldException , IllegalAccessException {
254+ // Test that temporary files use UUID-based naming for security
255+ String content = formFile ("file1" , "malicious../../../etc/passwd" , "content" ) +
256+ endline + "--" + boundary + "--" ;
257+
258+ mockRequest .setContent (content .getBytes (StandardCharsets .UTF_8 ));
259+
260+ // when
261+ multiPart .parse (mockRequest , tempDir );
262+
263+ // Access private field to get temporary files
264+ Field tempFilesField = JakartaMultiPartRequest .class .getDeclaredField ("temporaryFiles" );
265+ tempFilesField .setAccessible (true );
266+ @ SuppressWarnings ("unchecked" )
267+ List <File > temporaryFiles = (List <File >) tempFilesField .get (multiPart );
268+
269+ // then - verify secure naming prevents directory traversal
270+ assertThat (temporaryFiles ).isNotEmpty ();
271+ for (File tempFile : temporaryFiles ) {
272+ // Verify the temporary file uses secure UUID naming
273+ assertThat (tempFile .getName ()).startsWith ("upload_" );
274+ assertThat (tempFile .getName ()).endsWith (".tmp" );
275+ // Verify it doesn't contain malicious path elements
276+ assertThat (tempFile .getName ()).doesNotContain (".." );
277+ assertThat (tempFile .getName ()).doesNotContain ("/" );
278+ assertThat (tempFile .getName ()).doesNotContain ("\\ " );
279+ // Verify it's in the correct directory
280+ assertThat (tempFile .getParent ()).isEqualTo (tempDir );
281+ }
282+ }
283+
222284}
0 commit comments