Some checks failed
Tests / PHP 8.1 (push) Has been cancelled
Tests / PHP 8.2 (push) Has been cancelled
Tests / PHP 8.3 (push) Has been cancelled
103 lines
2.8 KiB
PHP
103 lines
2.8 KiB
PHP
<?php
|
|
|
|
namespace App\Jobs\Install;
|
|
|
|
use App\Abstracts\Job;
|
|
use Illuminate\Support\Facades\File;
|
|
use Illuminate\Support\Str;
|
|
|
|
class CopyFiles extends Job
|
|
{
|
|
protected $alias;
|
|
|
|
protected $path;
|
|
|
|
/**
|
|
* Create a new job instance.
|
|
*
|
|
* @param $alias
|
|
* @param $path
|
|
*/
|
|
public function __construct($alias, $path)
|
|
{
|
|
$this->alias = $alias;
|
|
$this->path = $path;
|
|
}
|
|
|
|
/**
|
|
* Execute the job.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function handle()
|
|
{
|
|
if (empty($this->path)) {
|
|
throw new \Exception(trans('modules.errors.file_copy', ['module' => $this->alias]));
|
|
}
|
|
|
|
$source = storage_path('app/temp/' . $this->path);
|
|
|
|
// Prevent path traversal: ensure source is strictly within the temp directory
|
|
$temp_base = realpath(storage_path('app/temp'));
|
|
$real_source = realpath($source);
|
|
|
|
if (! $this->isPathWithinTempBase($real_source, $temp_base)) {
|
|
throw new \Exception(trans('modules.errors.file_copy', ['module' => $this->alias]));
|
|
}
|
|
|
|
$destination = $this->getDestination($source);
|
|
|
|
// Move all files/folders from temp path
|
|
if (!File::copyDirectory($source, $destination)) {
|
|
throw new \Exception(trans('modules.errors.file_copy', ['module' => $this->alias]));
|
|
}
|
|
|
|
// Delete temp directory
|
|
File::deleteDirectory($source);
|
|
}
|
|
|
|
protected function isPathWithinTempBase($real_source, $temp_base)
|
|
{
|
|
if (($real_source === false) || ($temp_base === false)) {
|
|
return false;
|
|
}
|
|
|
|
$normalized_source = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $real_source);
|
|
$normalized_base = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $temp_base);
|
|
|
|
if (DIRECTORY_SEPARATOR === '\\') {
|
|
$normalized_source = mb_strtolower($normalized_source);
|
|
$normalized_base = mb_strtolower($normalized_base);
|
|
}
|
|
|
|
return str_starts_with(rtrim($normalized_source, DIRECTORY_SEPARATOR), rtrim($normalized_base, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR);
|
|
}
|
|
|
|
protected function getDestination($source)
|
|
{
|
|
if ($this->alias == 'core') {
|
|
return base_path();
|
|
}
|
|
|
|
if (!is_file($source . '/module.json')) {
|
|
throw new \Exception(trans('modules.errors.file_copy', ['module' => $this->alias]));
|
|
}
|
|
|
|
$modules_path = config('module.paths.modules');
|
|
|
|
// Create modules directory
|
|
if (!File::isDirectory($modules_path)) {
|
|
File::makeDirectory($modules_path);
|
|
}
|
|
|
|
$module_path = $modules_path . '/' . Str::studly($this->alias);
|
|
|
|
// Create module directory
|
|
if (!File::isDirectory($module_path)) {
|
|
File::makeDirectory($module_path);
|
|
}
|
|
|
|
return $module_path;
|
|
}
|
|
}
|