@@ -422,12 +422,38 @@ protected function handleBundleUpload(string $uploadId, UploadSession $session,
422422 $ fileIds = [];
423423 foreach ($ manifest ['files ' ] as $ fileInfo ) {
424424 $ filePath = $ fileInfo ['path ' ];
425- $ extractedFilePath = $ extractDir . '/ ' . $ filePath ;
425+
426+ // Sanitize the file path to prevent path traversal attacks
427+ // Remove ../ sequences and normalize path
428+ $ safePath = $ this ->sanitizeBundlePath ($ filePath );
429+ if ($ safePath === null ) {
430+ Log::warning ('tusd post-finish: Skipping file with dangerous path ' , [
431+ 'upload_id ' => $ uploadId ,
432+ 'file_path ' => $ filePath
433+ ]);
434+ continue ;
435+ }
436+
437+ $ extractedFilePath = $ extractDir . '/ ' . $ safePath ;
438+
439+ // Verify the resolved path is within the extraction directory
440+ $ resolvedPath = realpath ($ extractedFilePath );
441+ $ resolvedExtractDir = realpath ($ extractDir );
442+
443+ if ($ resolvedPath === false || $ resolvedExtractDir === false ||
444+ strpos ($ resolvedPath , $ resolvedExtractDir ) !== 0 ) {
445+ Log::warning ('tusd post-finish: Path traversal attempt in bundle ' , [
446+ 'upload_id ' => $ uploadId ,
447+ 'file_path ' => $ filePath ,
448+ 'resolved_path ' => $ resolvedPath
449+ ]);
450+ continue ;
451+ }
426452
427453 if (!file_exists ($ extractedFilePath )) {
428454 Log::warning ('tusd post-finish: Bundle file not found after extraction ' , [
429455 'upload_id ' => $ uploadId ,
430- 'file_path ' => $ filePath
456+ 'file_path ' => $ safePath
431457 ]);
432458 continue ;
433459 }
@@ -440,7 +466,7 @@ protected function handleBundleUpload(string $uploadId, UploadSession $session,
440466 'original_name ' => $ fileInfo ['originalName ' ],
441467 'type ' => $ fileInfo ['type ' ] ?? 'application/octet-stream ' ,
442468 'size ' => $ fileInfo ['size ' ],
443- 'temp_path ' => 'uploads/ ' . $ uploadId . '_extracted/ ' . $ filePath
469+ 'temp_path ' => 'uploads/ ' . $ uploadId . '_extracted/ ' . $ safePath
444470 ]);
445471
446472 $ fileIds [] = $ file ->id ;
@@ -531,6 +557,37 @@ protected function postTerminate(Request $request, array $payload)
531557 }
532558 }
533559
560+ /**
561+ * Sanitize a bundle file path to prevent path traversal attacks
562+ * Returns null if the path is dangerous and should be skipped
563+ */
564+ private function sanitizeBundlePath (string $ path ): ?string
565+ {
566+ // Normalize directory separators
567+ $ path = str_replace ('\\' , '/ ' , $ path );
568+
569+ // Check for dangerous patterns before sanitization
570+ if (strpos ($ path , '.. ' ) !== false ) {
571+ return null ;
572+ }
573+
574+ // Remove leading slashes
575+ $ path = ltrim ($ path , '/ ' );
576+
577+ // Remove null bytes
578+ $ path = str_replace ("\0" , '' , $ path );
579+
580+ // Clean up double slashes
581+ $ path = preg_replace ('/\/+/ ' , '/ ' , $ path );
582+
583+ // If empty after sanitization, reject
584+ if (empty ($ path )) {
585+ return null ;
586+ }
587+
588+ return $ path ;
589+ }
590+
534591 /**
535592 * Format bytes into human readable format
536593 */
0 commit comments