mirror of
https://github.com/coollabsio/coolify.git
synced 2026-03-11 08:55:47 +00:00
fix(security): Enhance file upload security and cleanup processes for terminal file upload
This commit is contained in:
parent
72020a7363
commit
903621dbaa
4 changed files with 33 additions and 11 deletions
|
|
@ -6,6 +6,7 @@ use Illuminate\Http\Request;
|
|||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Routing\Controller as BaseController;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Str;
|
||||
use Pion\Laravel\ChunkUpload\Exceptions\UploadMissingFileException;
|
||||
use Pion\Laravel\ChunkUpload\Handler\HandlerFactory;
|
||||
use Pion\Laravel\ChunkUpload\Receiver\FileReceiver;
|
||||
|
|
@ -114,13 +115,28 @@ class UploadController extends BaseController
|
|||
mkdir($finalPath, 0755, true);
|
||||
}
|
||||
|
||||
// Use original filename with timestamp to avoid conflicts
|
||||
$filename = time().'_'.$file->getClientOriginalName();
|
||||
$file->move($finalPath, $filename);
|
||||
// Security: Generate safe filename server-side to prevent path traversal
|
||||
$originalName = $file->getClientOriginalName();
|
||||
$extension = $file->getClientOriginalExtension();
|
||||
|
||||
// Create a safe slug from original filename (without extension)
|
||||
$nameWithoutExt = pathinfo($originalName, PATHINFO_FILENAME);
|
||||
$safeSlug = Str::slug($nameWithoutExt); // Converts to lowercase, replaces special chars with dashes
|
||||
$safeSlug = substr($safeSlug, 0, 50); // Limit length
|
||||
|
||||
// Sanitize extension (only allow alphanumeric)
|
||||
$safeExtension = preg_replace('/[^a-zA-Z0-9]/', '', $extension);
|
||||
|
||||
// Generate safe filename: timestamp_slug_randomhash.ext
|
||||
$randomHash = Str::random(16);
|
||||
$safeFilename = time().'_'.$safeSlug.'_'.$randomHash.($safeExtension ? '.'.$safeExtension : '');
|
||||
|
||||
$file->move($finalPath, $safeFilename);
|
||||
|
||||
return response()->json([
|
||||
'mime_type' => $mime,
|
||||
'filename' => $filename,
|
||||
'filename' => $safeFilename,
|
||||
'original_name' => $originalName, // Keep original name for reference
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,9 +41,10 @@ class CleanupExpiredTerminalFilesJob implements ShouldQueue
|
|||
// Delete file from server
|
||||
$server = Server::find($this->serverId);
|
||||
if ($server) {
|
||||
// Remove from server
|
||||
// Remove from server - escape shell arguments to prevent injection
|
||||
$escapedServerPath = escapeshellarg($this->serverPath);
|
||||
$result = instant_remote_process([
|
||||
"rm -f {$this->serverPath}"
|
||||
"rm -f {$escapedServerPath}"
|
||||
], $server, throwError: false);
|
||||
|
||||
if ($result) {
|
||||
|
|
@ -52,9 +53,12 @@ class CleanupExpiredTerminalFilesJob implements ShouldQueue
|
|||
|
||||
// If container was specified, remove from container as well
|
||||
if ($this->containerUuid) {
|
||||
$containerPath = "/tmp/{$this->filename}";
|
||||
$escapedContainerUuid = escapeshellarg($this->containerUuid);
|
||||
$escapedFilename = escapeshellarg($this->filename);
|
||||
$containerPath = "/tmp/{$this->filename}"; // For logging only
|
||||
|
||||
instant_remote_process([
|
||||
"docker exec {$this->containerUuid} rm -f {$containerPath} 2>/dev/null || true"
|
||||
"docker exec {$escapedContainerUuid} rm -f /tmp/{$escapedFilename} 2>/dev/null || true"
|
||||
], $server, throwError: false);
|
||||
|
||||
Log::info("Cleaned up container terminal file: {$containerPath}");
|
||||
|
|
|
|||
|
|
@ -130,14 +130,17 @@ class FileImport extends Component
|
|||
|
||||
// Copy file to server's temporary directory
|
||||
$serverTmpPath = "/tmp/coolify_import_{$uploadId}_{$sanitizedFilename}";
|
||||
instant_scp($finalPath, $serverTmpPath, $server);
|
||||
$safeServerTmpPath = escapeshellarg($serverTmpPath);
|
||||
instant_scp($finalPath, $safeServerTmpPath, $server);
|
||||
|
||||
// If it's a container, copy to container
|
||||
if ($isContainer) {
|
||||
$containerPath = "/tmp/{$sanitizedFilename}";
|
||||
$safeContainer = escapeshellarg($this->selectedUuid);
|
||||
$safeContainerPath = escapeshellarg($containerPath);
|
||||
|
||||
instant_remote_process([
|
||||
"docker cp {$serverTmpPath} {$this->selectedUuid}:{$containerPath}",
|
||||
"docker cp {$safeServerTmpPath} {$safeContainer}:{$safeContainerPath}",
|
||||
], $server);
|
||||
|
||||
$this->filePath = $containerPath;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
<div x-data="{ error: $wire.entangle('error'), filesize: $wire.entangle('filesize'), filename: $wire.entangle('filename'), isUploading: $wire.entangle('isUploading'), progress: $wire.entangle('progress'), filePath: $wire.entangle('filePath') }">
|
||||
|
||||
<div class="pb-4">
|
||||
<h2>Import File for Terminal</h2>
|
||||
<div class="text-sm text-neutral-500 pb-2">
|
||||
Upload a file that will be temporarily stored and accessible in your selected server or container.
|
||||
Perfect for importing SQL dumps, configuration files, or any other data.
|
||||
|
|
|
|||
Loading…
Reference in a new issue