From 87f7275f3182b14402314ff85e3ed4aeeaf1789f Mon Sep 17 00:00:00 2001 From: Andrew DalPino Date: Sun, 6 Sep 2026 22:30:20 -0500 Subject: [PATCH 1/3] Initial commit --- src/Extractors/SQLTable.php | 14 ++++++++++++-- tests/Extractors/SQLTableTest.php | 13 +++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/Extractors/SQLTable.php b/src/Extractors/SQLTable.php index 50359addf..9918c2780 100644 --- a/src/Extractors/SQLTable.php +++ b/src/Extractors/SQLTable.php @@ -11,6 +11,7 @@ use function Rubix\ML\iterator_first; use function count; use function array_keys; +use function preg_match; /** * SQL Table @@ -25,6 +26,11 @@ */ class SQLTable implements Extractor { + /** + * The regex pattern for validating table names. + */ + protected const string TABLE_NAME_PATTERN = '/^[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)?$/'; + /** * The PDO connection to the database. * @@ -60,13 +66,17 @@ public function __construct(PDO $connection, string $table, int $batchSize = 256 throw new InvalidArgumentException('Table name cannot be empty.'); } + if (!preg_match(self::TABLE_NAME_PATTERN, $table)) { + throw new InvalidArgumentException("Table name '{$table}' is not a valid identifier."); + } + if ($batchSize < 1) { throw new InvalidArgumentException('Batch size must be' . " greater than 0, $batchSize given."); } $this->connection = $connection; - $this->table = $connection->quote($table); + $this->table = $table; $this->batchSize = $batchSize; } @@ -87,7 +97,7 @@ public function header() : array */ public function getIterator() : Traversable { - $query = "SELECT * FROM {$this->table} LIMIT :offset, {$this->batchSize}"; + $query = "SELECT * FROM {$this->table} LIMIT {$this->batchSize} OFFSET :offset"; $statement = $this->connection->prepare($query); diff --git a/tests/Extractors/SQLTableTest.php b/tests/Extractors/SQLTableTest.php index 38832c8fb..f54de6ea0 100644 --- a/tests/Extractors/SQLTableTest.php +++ b/tests/Extractors/SQLTableTest.php @@ -4,6 +4,7 @@ use Rubix\ML\Extractors\SQLTable; use Rubix\ML\Extractors\Extractor; +use Rubix\ML\Exceptions\InvalidArgumentException; use PHPUnit\Framework\TestCase; use IteratorAggregate; use Traversable; @@ -68,4 +69,16 @@ public function extract() : void $this->assertEquals($expected, $header); } + + /** + * @test + */ + public function rejectInvalidIdentifier() : void + { + $connection = new PDO('sqlite::memory:'); + + $this->expectException(InvalidArgumentException::class); + + new SQLTable($connection, "pets'; DROP TABLE users; --"); + } } From f974bba67b15efe89a711d5fb9912b7eee87c681 Mon Sep 17 00:00:00 2001 From: Andrew DalPino Date: Sun, 6 Sep 2026 22:40:50 -0500 Subject: [PATCH 2/3] Update CHANGELOG --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 412376cd5..dc81e41cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +- 2.5.13 + - Fix SQL Table offset syntax to work with Postgres + - Prevent SQL Table injection attack + - 2.5.12 - Fix Adaboost proba() probability normalization - Optimize minmax operations From 64a94b4a2c6e71ffe719d9faeb29aaa224345949 Mon Sep 17 00:00:00 2001 From: Andrew DalPino Date: Sun, 6 Sep 2026 22:41:54 -0500 Subject: [PATCH 3/3] Fix constant declaration for TABLE_NAME_PATTERN Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/Extractors/SQLTable.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Extractors/SQLTable.php b/src/Extractors/SQLTable.php index 9918c2780..e68d97080 100644 --- a/src/Extractors/SQLTable.php +++ b/src/Extractors/SQLTable.php @@ -26,10 +26,7 @@ */ class SQLTable implements Extractor { - /** - * The regex pattern for validating table names. - */ - protected const string TABLE_NAME_PATTERN = '/^[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)?$/'; + protected const TABLE_NAME_PATTERN = '/^[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)?$/'; /** * The PDO connection to the database.