diff --git a/src/Exceptions/CredentialExposure.php b/src/Exceptions/CredentialExposure.php new file mode 100644 index 00000000..6a6a49e4 --- /dev/null +++ b/src/Exceptions/CredentialExposure.php @@ -0,0 +1,13 @@ +getMessage(), $exception->getCode(), $exception); + } +} diff --git a/src/Tasks/Backup/BackupJob.php b/src/Tasks/Backup/BackupJob.php index a556414b..39661594 100644 --- a/src/Tasks/Backup/BackupJob.php +++ b/src/Tasks/Backup/BackupJob.php @@ -16,6 +16,7 @@ use Spatie\Backup\Events\DumpingDatabase; use Spatie\Backup\Exceptions\BackupFailed; use Spatie\Backup\Exceptions\InvalidBackupJob; +use Spatie\DbDumper\Compressors\GzipCompressor; use Spatie\DbDumper\Databases\MongoDb; use Spatie\DbDumper\Databases\Sqlite; use Spatie\DbDumper\DbDumper; @@ -155,6 +156,14 @@ public function run(): void ->force() ->create() ->empty(); + $cleanupRegistered = false; + $shutdownHandler = function () use (&$cleanupRegistered) { + if ($cleanupRegistered && $this->temporaryDirectory->exists()) { + $this->temporaryDirectory->delete(); + } + }; + register_shutdown_function($shutdownHandler); + $cleanupRegistered = true; if ($this->signals) { Signal::handle(SIGINT, function (Command $command) { @@ -187,6 +196,7 @@ public function run(): void } $this->temporaryDirectory->delete(); + $cleanupRegistered = false; // Prevent double cleanup if ($this->signals) { Signal::clearHandlers(SIGINT); diff --git a/src/Tasks/Backup/DbDumperFactory.php b/src/Tasks/Backup/DbDumperFactory.php index a7e0e080..40f2d617 100644 --- a/src/Tasks/Backup/DbDumperFactory.php +++ b/src/Tasks/Backup/DbDumperFactory.php @@ -58,13 +58,17 @@ public static function createFromConnection(string $dbConnectionName): DbDumper } if (isset($dbConfig['port'])) { - if (filter_var($dbConfig['port'], FILTER_VALIDATE_INT, [ + $port = $dbConfig['port']; + if ($port === '' || $port === null) { + } elseif (filter_var($port, FILTER_VALIDATE_INT, [ 'options' => [ 'min_range' => 1, 'max_range' => 65535, ], ]) !== false) { - $dbDumper = $dbDumper->setPort((int) $dbConfig['port']); + $dbDumper = $dbDumper->setPort((int) $port); + } else { + consoleOutput()->warn("Invalid port value '{$port}' for database connection '{$dbConnectionName}'. Using default port."); } } diff --git a/src/Tasks/Backup/FileSelection.php b/src/Tasks/Backup/FileSelection.php index e9325340..25563fe4 100644 --- a/src/Tasks/Backup/FileSelection.php +++ b/src/Tasks/Backup/FileSelection.php @@ -111,14 +111,20 @@ protected function includedDirectories(): array protected function shouldExclude(string $path): bool { - $path = realpath($path); - if (is_dir($path)) { - $path .= DIRECTORY_SEPARATOR; + $realPath = realpath($path); + + if ($realPath === false) { + consoleOutput()->warn("Cannot resolve path: {$path}. Skipping..."); + return false; + } + + if (is_dir($realPath)) { + $realPath .= DIRECTORY_SEPARATOR; } foreach ($this->excludeFilesAndDirectories as $excludedPath) { - if (Str::startsWith($path, $excludedPath.(is_dir($excludedPath) ? DIRECTORY_SEPARATOR : ''))) { - if ($path != $excludedPath && is_file($excludedPath)) { + if (Str::startsWith($realPath, $excludedPath.(is_dir($excludedPath) ? DIRECTORY_SEPARATOR : ''))) { + if ($realPath != $excludedPath && is_file($excludedPath)) { continue; } @@ -137,7 +143,13 @@ protected function sanitize(string|array $paths): Collection return collect($paths) ->reject(fn (string $path) => $path === '') ->flatMap(fn (string $path) => $this->getMatchingPaths($path)) - ->map(fn (string $path) => realpath($path)) + ->map(function (string $path) { + $realPath = realpath($path); + if ($realPath === false) { + consoleOutput()->warn("Cannot resolve path: {$path}. This path will be excluded from backup."); + } + return $realPath; + }) ->reject(fn ($path) => $path === false); }