From 48b48379059c7dcc9f253c8b7aa33a757f751e82 Mon Sep 17 00:00:00 2001 From: lukinovec Date: Fri, 1 May 2026 14:09:56 +0200 Subject: [PATCH] Validate in-memory db names, move SQLite-specific methods to the SQLiteManager --- .../Concerns/ValidatesDatabaseParameters.php | 36 ++------------ .../SQLiteDatabaseManager.php | 48 ++++++++++++++++++- 2 files changed, 49 insertions(+), 35 deletions(-) diff --git a/src/Database/Concerns/ValidatesDatabaseParameters.php b/src/Database/Concerns/ValidatesDatabaseParameters.php index ba6a7cbf..0a8e6d9d 100644 --- a/src/Database/Concerns/ValidatesDatabaseParameters.php +++ b/src/Database/Concerns/ValidatesDatabaseParameters.php @@ -28,16 +28,6 @@ trait ValidatesDatabaseParameters return 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-'; } - /** - * Characters allowed in filenames (SQLite databases). - * - * Includes dots to support file extensions (e.g. '.sqlite'). - */ - protected static function allowedFilenameCharacters(): string - { - return 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-.'; - } - /** * Characters allowed in database user passwords. * @@ -62,7 +52,7 @@ trait ValidatesDatabaseParameters * * @throws InvalidArgumentException */ - protected function validateParameter(string|array|null $parameters, string|null $allowedCharacters = null): void + protected static function validateParameter(string|array|null $parameters, string|null $allowedCharacters = null): void { $allowedCharacters ??= static::allowedParameterCharacters(); @@ -95,28 +85,8 @@ trait ValidatesDatabaseParameters * * @throws InvalidArgumentException */ - protected function validatePassword(string|null $password): void + protected static function validatePassword(string|null $password): void { - $this->validateParameter($password, static::allowedPasswordCharacters()); - } - - /** - * Ensure filename only contains allowed characters (static::allowedFilenameCharacters()) - * and is not a directory name before used in file paths (e.g. SQLite database names). - * - * @throws InvalidArgumentException - * @see Stancl\Tenancy\Database\TenantDatabaseManagers\SQLiteDatabaseManager - */ - protected function validateFilename(string $filename): void - { - $this->validateParameter($filename, static::allowedFilenameCharacters()); - - if ($filename === '') { - throw new InvalidArgumentException('Filename cannot be empty.'); - } - - if (is_dir($filename)) { - throw new InvalidArgumentException("Filename ('{$filename}') cannot be a directory."); - } + static::validateParameter($password, allowedCharacters: static::allowedPasswordCharacters()); } } diff --git a/src/Database/TenantDatabaseManagers/SQLiteDatabaseManager.php b/src/Database/TenantDatabaseManagers/SQLiteDatabaseManager.php index 9df56ccb..657d0c1b 100644 --- a/src/Database/TenantDatabaseManagers/SQLiteDatabaseManager.php +++ b/src/Database/TenantDatabaseManagers/SQLiteDatabaseManager.php @@ -11,6 +11,7 @@ use Stancl\Tenancy\Database\Concerns\ValidatesDatabaseParameters; use Stancl\Tenancy\Database\Contracts\TenantDatabaseManager; use Stancl\Tenancy\Database\Contracts\TenantWithDatabase; use Throwable; +use InvalidArgumentException; class SQLiteDatabaseManager implements TenantDatabaseManager { @@ -60,6 +61,16 @@ class SQLiteDatabaseManager implements TenantDatabaseManager */ public static Closure|null $closeInMemoryConnectionUsing = null; + /** + * Characters allowed in database names. + * + * Includes dots to support file extensions (e.g. '.sqlite'). + */ + protected static function allowedDatabaseNameCharacters(): string + { + return 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-.'; + } + public function createDatabase(TenantWithDatabase $tenant): bool { /** @var TenantWithDatabase&Model $tenant */ @@ -136,6 +147,8 @@ class SQLiteDatabaseManager implements TenantDatabaseManager (static::$persistInMemoryConnectionUsing)(new PDO($dsn), $dsn); } } else { + $this->validateDatabaseName($databaseName); + $baseConfig['database'] = database_path($databaseName); } @@ -148,13 +161,44 @@ class SQLiteDatabaseManager implements TenantDatabaseManager return rtrim(static::$path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $name; } - $this->validateFilename($name); + $this->validateDatabaseName($name); return database_path($name); } public static function isInMemory(string $name): bool { - return $name === ':memory:' || str_starts_with($name, 'file:_tenancy_inmemory_'); + if ($name === ':memory:') { + return true; + } + + if (str_starts_with($name, 'file:_tenancy_inmemory_') && + str_ends_with($name, '?mode=memory&cache=shared')) { + // Named in-memory DBs are formatted like 'file:_tenancy_inmemory_tenant123?mode=memory&cache=shared' + static::validateDatabaseName($name, ':?=&'); + + return true; + } + + return false; + } + + /** + * Ensure database name only contains allowed characters + * (static::allowedDatabaseNameCharacters() + $extraAllowedCharacters) and is not a directory name. + * + * @throws InvalidArgumentException + */ + protected static function validateDatabaseName(string $name, string $extraAllowedCharacters = ''): void + { + static::validateParameter($name, static::allowedDatabaseNameCharacters() . $extraAllowedCharacters); + + if ($name === '') { + throw new InvalidArgumentException('Database name cannot be empty.'); + } + + if (is_dir($name)) { + throw new InvalidArgumentException("Database name cannot be a directory."); + } } }