@@ -540,7 +540,7 @@ public function testDownloadsToFileShouldNotCreateFileWhenObjectNotFound()
540540 $ this ->connection ->downloadObject (Argument::any ())
541541 ->willThrow (new NotFoundException ($ exceptionString ));
542542 $ object = 'non_existent_object.txt ' ;
543- $ downloadFilePath = __DIR__ . ' / storage_test_downloads_to_file.txt ' ;
543+ $ downloadFilePath = ' storage_test_downloads_to_file.txt ' ;
544544 $ object = new StorageObject ($ this ->connection ->reveal (), $ object , self ::BUCKET );
545545 $ throws = false ;
546546 try {
@@ -554,6 +554,33 @@ public function testDownloadsToFileShouldNotCreateFileWhenObjectNotFound()
554554 $ this ->assertFileDoesNotExist ($ downloadFilePath );
555555 }
556556
557+ /**
558+ * @dataProvider provideInvalidFilePaths
559+ */
560+ public function testDownloadsToFileShouldBlockPathTraversal (string $ invalidFilePath )
561+ {
562+ $ this ->expectException (\RuntimeException::class);
563+ $ this ->expectExceptionMessage (
564+ 'Path traversal is not allowed. File path is outside the designated directory. '
565+ );
566+ $ stream = Utils::streamFor ('mock content ' );
567+ $ this ->connection ->downloadObject (Argument::any ())
568+ ->willReturn ($ stream );
569+ $ objectName = 'storage_test_downloads_to_file.txt ' ;
570+ $ object = new StorageObject ($ this ->connection ->reveal (), $ objectName , self ::BUCKET );
571+
572+ $ object ->downloadToFile ($ invalidFilePath . $ objectName );
573+ }
574+
575+ public function provideInvalidFilePaths ()
576+ {
577+ return [
578+ ['storage/../.. ' ], // relative traversal
579+ ['/storage/ ' ], // absolute filepath
580+ ['C:/storage/ ' ], // windows drive letters
581+ ];
582+ }
583+
557584 public function testDownloadAsStreamWithoutExtraOptions ()
558585 {
559586 $ bucket = 'bucket ' ;
0 commit comments