diff --git a/config/file-manager.php b/config/file-manager.php index 63fd749..9697c0c 100644 --- a/config/file-manager.php +++ b/config/file-manager.php @@ -84,6 +84,52 @@ */ 'allowFileTypes' => [], + /** + * File upload - disallow these executable file types + * + * [] - no restrictions + */ + 'disallowFileTypes' => [ + 'php', + 'php3', + 'php4', + 'php5', + 'phtml', + 'js', + 'html', + 'htm', + 'xhtml', + 'shtml', + 'jhtml', + 'pl', + 'py', + 'cgi', + 'exe', + ], + + /** + * File upload - disallow these executable file mimetypes + * + * [] - no restrictions + */ + 'disallowFileMimeTypes' => [ + 'text/x-php', + 'text/html', + 'text/javascript', + 'application/x-javascript', + 'text/x-javascript', + 'application/javascript', + 'application/x-sh', + 'text/x-python', + 'application/x-python', + 'text/x-perl', + 'application/x-perl', + 'application/x-httpd-cgi', + 'application/x-executable', + 'application/x-msdownload', + 'application/octet-stream', + ], + /** * Show / Hide system files and folders */ diff --git a/src/Events/FilesUploaded.php b/src/Events/FilesUploaded.php index 69be788..ebd4d19 100644 --- a/src/Events/FilesUploaded.php +++ b/src/Events/FilesUploaded.php @@ -17,7 +17,7 @@ class FilesUploaded private $path; /** - * @var \Illuminate\Http\UploadedFile + * @var \Illuminate\Http\UploadedFile[] */ private $files; diff --git a/src/Events/FilesUploading.php b/src/Events/FilesUploading.php index 6f51484..c7f3c24 100644 --- a/src/Events/FilesUploading.php +++ b/src/Events/FilesUploading.php @@ -17,7 +17,7 @@ class FilesUploading private $path; /** - * @var \Illuminate\Http\UploadedFile + * @var \Illuminate\Http\UploadedFile[] */ private $files; diff --git a/src/FileManager.php b/src/FileManager.php index aa879cd..aacf68f 100644 --- a/src/FileManager.php +++ b/src/FileManager.php @@ -167,15 +167,37 @@ public function upload($disk, $path, $files, $overwrite): array continue; } + // check file disallow type + if ($this->configRepository->getDisallowFileTypes() + && in_array( + $file->getClientOriginalExtension(), + $this->configRepository->getDisallowFileTypes() + ) + ) { + $fileNotUploaded = true; + continue; + } + + // check file mime type + if ($this->configRepository->getDisallowFileMimeTypes() + && in_array( + $file->getMimeType(), + $this->configRepository->getDisallowFileMimeTypes() + ) + ) { + $fileNotUploaded = true; + continue; + } + $name = $file->getClientOriginalName(); if ($this->configRepository->getSlugifyNames()) { $name = Str::slug( - Str::replace( - '.' . $file->getClientOriginalExtension(), - '', - $name - ) - ) . '.' . $file->getClientOriginalExtension(); + Str::replace( + '.' . $file->getClientOriginalExtension(), + '', + $name + ) + ) . '.' . $file->getClientOriginalExtension(); } // overwrite or save file Storage::disk($disk)->putFileAs( @@ -273,6 +295,15 @@ public function paste($disk, $path, $clipboard): array */ public function rename($disk, $newName, $oldName): array { + if (!$this->AllowTypeFileName($newName) || !$this->DisallowTypeFileName($newName)) { + return [ + 'result' => [ + 'status' => 'error', + 'message' => "Failed to create file because extension is not allowed", + ], + ]; + } + Storage::disk($disk)->move($oldName, $newName); return [ @@ -414,6 +445,15 @@ public function createDirectory($disk, $path, $name) */ public function createFile($disk, $path, $name): array { + if (!$this->AllowTypeFileName($name) || !$this->DisallowTypeFileName($name)) { + return [ + 'result' => [ + 'status' => 'error', + 'message' => "Failed to create file because extension is not allowed", + ], + ]; + } + $path = $this->newPath($path, $name); if (Storage::disk($disk)->exists($path)) { @@ -448,6 +488,15 @@ public function createFile($disk, $path, $name): array */ public function updateFile($disk, $path, $file): array { + if (!$this->AllowTypeFileName($file->getClientOriginalName()) || !$this->DisallowTypeFileName($file->getClientOriginalName())) { + return [ + 'result' => [ + 'status' => 'error', + 'message' => "Failed to create file because extension is not allowed", + ], + ]; + } + Storage::disk($disk)->putFileAs( $path, $file, @@ -485,4 +534,40 @@ public function streamFile($disk, $path): StreamedResponse return Storage::disk($disk)->response($path, $filename, ['Accept-Ranges' => 'bytes']); } + + private function AllowTypeFileName($name) + { + $ext = explode('.', $name); + $ext = end($ext); + + if ( + $this->configRepository->getAllowFileTypes() + && !in_array( + $ext, + $this->configRepository->getAllowFileTypes() + ) + ) { + return false; + } else { + return true; + } + } + + private function DisallowTypeFileName($name) + { + $ext = explode('.', $name); + $ext = end($ext); + + if ( + $this->configRepository->getDisallowFileTypes() + && in_array( + $ext, + $this->configRepository->getDisallowFileTypes() + ) + ) { + return false; + } else { + return true; + } + } } diff --git a/src/Services/ConfigService/ConfigRepository.php b/src/Services/ConfigService/ConfigRepository.php index 915b9ff..91d9d4c 100644 --- a/src/Services/ConfigService/ConfigRepository.php +++ b/src/Services/ConfigService/ConfigRepository.php @@ -87,6 +87,20 @@ public function getMaxUploadFileSize(): ?int; */ public function getAllowFileTypes(): array; + /** + * File upload - disallow these executable file types + * + * [] - no restrictions + */ + public function getDisallowFileTypes(): array; + + /** + * File upload - disallow these executable file mimetypes + * + * [] - no restrictions + */ + public function getDisallowFileMimeTypes(): array; + /** * Show / Hide system files and folders * diff --git a/src/Services/ConfigService/DefaultConfigRepository.php b/src/Services/ConfigService/DefaultConfigRepository.php index 2ea4b98..9b64cbd 100644 --- a/src/Services/ConfigService/DefaultConfigRepository.php +++ b/src/Services/ConfigService/DefaultConfigRepository.php @@ -114,6 +114,58 @@ final public function getAllowFileTypes(): array return config('file-manager.allowFileTypes'); } + /** + * File upload - disallow these executable file types + * + * [] - no restrictions + */ + final public function getDisallowFileTypes(): array + { + return config('file-manager.disallowFileTypes', [ + 'php', + 'php3', + 'php4', + 'php5', + 'phtml', + 'js', + 'html', + 'htm', + 'xhtml', + 'shtml', + 'jhtml', + 'pl', + 'py', + 'cgi', + 'exe', + ]); + } + + /** + * File upload - disallow these executable file mimetypes + * + * [] - no restrictions + */ + final public function getDisallowFileMimeTypes(): array + { + return config('file-manager.disallowFileMimeTypes', [ + 'text/x-php', + 'text/html', + 'text/javascript', + 'application/x-javascript', + 'text/x-javascript', + 'application/javascript', + 'application/x-sh', + 'text/x-python', + 'application/x-python', + 'text/x-perl', + 'application/x-perl', + 'application/x-httpd-cgi', + 'application/x-executable', + 'application/x-msdownload', + 'application/octet-stream', + ]); + } + /** * Show / Hide system files and folders * diff --git a/src/Services/Zip.php b/src/Services/Zip.php index bdd1583..f1ad508 100644 --- a/src/Services/Zip.php +++ b/src/Services/Zip.php @@ -6,6 +6,7 @@ use Alexusmai\LaravelFileManager\Events\UnzipFailed; use Alexusmai\LaravelFileManager\Events\ZipCreated; use Alexusmai\LaravelFileManager\Events\ZipFailed; +use Alexusmai\LaravelFileManager\Services\ConfigService\ConfigRepository; use Illuminate\Http\Request; use Illuminate\Support\Facades\Storage; use RecursiveIteratorIterator; @@ -15,6 +16,7 @@ class Zip { protected $zip; + protected $configRepository; protected $request; //protected $pathPrefix; @@ -22,16 +24,14 @@ class Zip * Zip constructor. * * @param ZipArchive $zip + * @param ConfigRepository $configRepository * @param Request $request */ - public function __construct(ZipArchive $zip, Request $request) + public function __construct(ZipArchive $zip, ConfigRepository $configRepository, Request $request) { $this->zip = $zip; $this->request = $request; - //$this->pathPrefix = Storage::disk($request->input('disk'))->path(); - //->getDriver() - //->getAdapter() - //->getPathPrefix(); + $this->configRepository = $configRepository; } /** @@ -140,23 +140,56 @@ protected function createArchive(): bool protected function extractArchive(): bool { $zipPath = $this->prefixer($this->request->input('path')); - $rootPath = dirname($zipPath); - - // extract to new folder $folder = $this->request->input('folder'); + $extractPath = $folder ? $rootPath.'/'.$folder : $rootPath; + + // Initialize file info for mime-type checking + $finfo = new \finfo(FILEINFO_MIME_TYPE); if ($this->zip->open($zipPath) === true) { - $this->zip->extractTo($folder ? $rootPath.'/'.$folder : $rootPath); + // Loop through each file in the ZIP archive + for ($i = 0; $i < $this->zip->numFiles; $i++) { + $fileInfo = $this->zip->statIndex($i); + $fileName = $fileInfo['name']; + + // Get the file contents + $fileContents = $this->zip->getFromIndex($i); + + // Check the MIME type of the file + $mimeType = $finfo->buffer($fileContents); + + // Skip extraction if the file extension is .php + if (in_array(pathinfo($fileName, PATHINFO_EXTENSION), $this->configRepository->getDisallowFileTypes())) { + // Optionally log or handle the ignored file + continue; + } + + // Skip extraction if the file MIME type is text/x-php + if (in_array($mimeType, $this->configRepository->getDisallowFileMimeTypes())) { + // Optionally log or handle the ignored file + continue; + } + + // Extract each file + $filePath = $extractPath . '/' . $fileName; + + // Ensure the directory exists + if (!file_exists(dirname($filePath))) { + mkdir(dirname($filePath), 0755, true); + } + + // Write the file + file_put_contents($filePath, $fileContents); + } + $this->zip->close(); event(new UnzipCreated($this->request)); - return true; } event(new UnzipFailed($this->request)); - return false; }