Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/mutation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,6 @@ jobs:

- name: Run infection.
run: |
vendor/bin/infection --threads=2 --ignore-msi-with-no-mutations
vendor/bin/infection --threads=1 --ignore-msi-with-no-mutations
env:
STRYKER_DASHBOARD_API_KEY: ${{ secrets.STRYKER_DASHBOARD_API_KEY }}
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- Enh #460: Remove unnecessary files from Composer package (@mspirkov)
- Enh #461: Add `ext-pdo_mysql` to `require` section of `composer.json` (@Tigrov)
- Enh #462: Remove `ext-ctype` from `require` section of `composer.json` (@Tigrov)
- Bug #463: Fix SQL injection in `Schema::findViewNames()` (@darkspock, @vjik)

## 2.0.0 December 05, 2025

Expand Down
19 changes: 11 additions & 8 deletions src/Schema.php
Original file line number Diff line number Diff line change
Expand Up @@ -187,17 +187,20 @@ protected function findTableNames(string $schema = ''): array

protected function findViewNames(string $schema = ''): array
{
$sql = match ($schema) {
'' => <<<SQL
if ($schema === '') {
$sql = <<<SQL
SELECT table_name FROM information_schema.tables WHERE table_type = 'VIEW' AND table_schema != 'sys' order by table_name
SQL,
default => <<<SQL
SELECT table_name FROM information_schema.tables WHERE table_type = 'VIEW' AND table_schema = '$schema' order by table_name
SQL,
};
SQL;
$params = [];
} else {
$sql = <<<SQL
SELECT table_name FROM information_schema.tables WHERE table_type = 'VIEW' AND table_schema = :schemaName order by table_name
SQL;
$params = [':schemaName' => $schema];
}

/** @var string[] */
return $this->db->createCommand($sql)->queryColumn();
return $this->db->createCommand($sql, $params)->queryColumn();
}

/**
Expand Down
37 changes: 36 additions & 1 deletion tests/SchemaTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -264,8 +264,43 @@ public function testGetViewNames(): void
};

$this->assertSame($viewExpected, $views);
}

$db->close();
public function testGetViewNamesWithSchema(): void
{
$db = TestConnection::create();

$schema1 = 'test_get_view_names_with_schema1';
$schema2 = 'test_get_view_names_with_schema2';

try {
$db->createCommand("CREATE DATABASE IF NOT EXISTS `$schema1`")->execute();
$db->createCommand("CREATE DATABASE IF NOT EXISTS `$schema2`")->execute();

$db->createCommand("CREATE OR REPLACE VIEW `$schema1`.`view_a` AS SELECT 1")->execute();
$db->createCommand("CREATE OR REPLACE VIEW `$schema1`.`view_b` AS SELECT 1")->execute();
$db->createCommand("CREATE OR REPLACE VIEW `$schema2`.`view_c` AS SELECT 1")->execute();

$schema = $db->getSchema();

$this->assertSame(['view_a', 'view_b'], $schema->getViewNames($schema1));
$this->assertSame(['view_c'], $schema->getViewNames($schema2));
} finally {
$db->createCommand("DROP DATABASE IF EXISTS `$schema1`")->execute();
$db->createCommand("DROP DATABASE IF EXISTS `$schema2`")->execute();
$db->close();
}
}

public function testGetViewNamesSqlInjection(): void
{
$db = $this->getSharedConnection();
$this->loadFixture();

$schema = $db->getSchema();
$views = $schema->getViewNames("' OR ''='");

$this->assertSame([], $views);
}

#[DataProviderExternal(SchemaProvider::class, 'constraints')]
Expand Down
Loading