Skip to content
46 changes: 46 additions & 0 deletions config/file-manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down
2 changes: 1 addition & 1 deletion src/Events/FilesUploaded.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class FilesUploaded
private $path;

/**
* @var \Illuminate\Http\UploadedFile
* @var \Illuminate\Http\UploadedFile[]
*/
private $files;

Expand Down
2 changes: 1 addition & 1 deletion src/Events/FilesUploading.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class FilesUploading
private $path;

/**
* @var \Illuminate\Http\UploadedFile
* @var \Illuminate\Http\UploadedFile[]
*/
private $files;

Expand Down
97 changes: 91 additions & 6 deletions src/FileManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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 [
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
}
}
}
14 changes: 14 additions & 0 deletions src/Services/ConfigService/ConfigRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand Down
52 changes: 52 additions & 0 deletions src/Services/ConfigService/DefaultConfigRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand Down
55 changes: 44 additions & 11 deletions src/Services/Zip.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -15,23 +16,22 @@
class Zip
{
protected $zip;
protected $configRepository;
protected $request;
//protected $pathPrefix;

/**
* 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;
}

/**
Expand Down Expand Up @@ -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;
}

Expand Down