1
0
Fork 0
mirror of https://github.com/archtechx/tenancy.git synced 2026-05-06 18:04:03 +00:00

Add validateFilename()

Use validateFilename instead of validateParameter in SQLiteDatabaseManager. Directories are no longer considered valid SQLite database names.
This commit is contained in:
lukinovec 2026-05-01 09:03:50 +02:00
parent 2bd3a868ec
commit 76c324d758
3 changed files with 37 additions and 18 deletions

View file

@ -28,6 +28,16 @@ trait ValidatesDatabaseParameters
return 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-'; return 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-';
} }
/**
* Characters allowed in filenames (SQLite databases).
*
* Allows dots to support file extensions (e.g. '.sqlite').
*/
protected static function filenameAllowlist(): string
{
return 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-.';
}
/** /**
* Characters allowed in database user passwords. * Characters allowed in database user passwords.
* *
@ -66,7 +76,7 @@ trait ValidatesDatabaseParameters
foreach (str_split($parameter) as $char) { foreach (str_split($parameter) as $char) {
if (! str_contains($allowlist, $char)) { if (! str_contains($allowlist, $char)) {
throw new InvalidArgumentException("Forbidden character '{$char}' in database parameter."); throw new InvalidArgumentException("Forbidden character '{$char}' in parameter.");
} }
} }
} }
@ -75,8 +85,8 @@ trait ValidatesDatabaseParameters
/** /**
* Ensure password only contains allowed characters before used in SQL statements. * Ensure password only contains allowed characters before used in SQL statements.
* *
* Used as a shorthand for calling validateParameter() with the less strict allowlist * Used in permission controlled managers as a shorthand for calling validateParameter()
* to validate database user passwords. * with the less strict allowlist to validate database user passwords.
* *
* @throws InvalidArgumentException * @throws InvalidArgumentException
*/ */
@ -84,4 +94,20 @@ trait ValidatesDatabaseParameters
{ {
$this->validateParameter($password, static::passwordAllowlist()); $this->validateParameter($password, static::passwordAllowlist());
} }
/**
* Ensure filename only contains allowed characters and is not a directory name
* before used in file paths (e.g. SQLite databases).
*
* @throws InvalidArgumentException
* @see Stancl\Tenancy\Database\TenantDatabaseManagers\SQLiteDatabaseManager
*/
protected function validateFilename(string|null $filename): void
{
if (is_dir($filename)) {
throw new InvalidArgumentException("Filename '{$filename}' is a directory.");
}
$this->validateParameter($filename, static::filenameAllowlist());
}
} }

View file

@ -60,11 +60,6 @@ class SQLiteDatabaseManager implements TenantDatabaseManager
*/ */
public static Closure|null $closeInMemoryConnectionUsing = null; public static Closure|null $closeInMemoryConnectionUsing = null;
protected static function parameterAllowlist(): string
{
return 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-.';
}
public function createDatabase(TenantWithDatabase $tenant): bool public function createDatabase(TenantWithDatabase $tenant): bool
{ {
/** @var TenantWithDatabase&Model $tenant */ /** @var TenantWithDatabase&Model $tenant */
@ -92,8 +87,6 @@ class SQLiteDatabaseManager implements TenantDatabaseManager
return true; return true;
} }
$this->validateParameter($name);
return file_put_contents($this->getPath($name), '') !== false; return file_put_contents($this->getPath($name), '') !== false;
} }
@ -109,8 +102,6 @@ class SQLiteDatabaseManager implements TenantDatabaseManager
return true; return true;
} }
$this->validateParameter($name);
$path = $this->getPath($name); $path = $this->getPath($name);
try { try {
@ -132,8 +123,6 @@ class SQLiteDatabaseManager implements TenantDatabaseManager
return true; return true;
} }
$this->validateParameter($name);
return file_exists($this->getPath($name)); return file_exists($this->getPath($name));
} }
@ -159,6 +148,8 @@ class SQLiteDatabaseManager implements TenantDatabaseManager
return rtrim(static::$path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $name; return rtrim(static::$path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $name;
} }
$this->validateFilename($name);
return database_path($name); return database_path($name);
} }

View file

@ -615,15 +615,17 @@ test('database managers validate parameters that cannot be bound', function ($dr
} }
})->with('database_managers'); })->with('database_managers');
test('sqlite database manager validates the name in databaseExists', function () { test('sqlite database manager validates database filenames', function () {
$manager = app(SQLiteDatabaseManager::class); $manager = app(SQLiteDatabaseManager::class);
expect(fn () => $manager->databaseExists("../invalid-db-name.sqlite")) // Dots are allowed in database names
->toThrow(InvalidArgumentException::class);
expect(fn () => $manager->databaseExists('valid-db_name.sqlite')) expect(fn () => $manager->databaseExists('valid-db_name.sqlite'))
->not()->toThrow(InvalidArgumentException::class); ->not()->toThrow(InvalidArgumentException::class);
// Directories are not allowed as database names
expect(fn () => $manager->databaseExists(".."))
->toThrow(InvalidArgumentException::class);
// In-memory database names aren't validated // In-memory database names aren't validated
expect(fn () => $manager->databaseExists('../_tenancy_inmemory_')) expect(fn () => $manager->databaseExists('../_tenancy_inmemory_'))
->not()->toThrow(InvalidArgumentException::class); ->not()->toThrow(InvalidArgumentException::class);