From 0d2639bb2353343774ce96f66e62cac18a2f7597 Mon Sep 17 00:00:00 2001 From: Bogdan Date: Wed, 8 Jul 2026 18:24:55 +0200 Subject: [PATCH 1/2] Fix SQL injection in QueueJobModel priority sorting and add tests --- src/Models/QueueJobModel.php | 9 ++++----- tests/Models/QueueJobModelTest.php | 21 +++++++++++++++++++++ 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/Models/QueueJobModel.php b/src/Models/QueueJobModel.php index a61e5ee..1716ac3 100644 --- a/src/Models/QueueJobModel.php +++ b/src/Models/QueueJobModel.php @@ -134,12 +134,14 @@ private function setPriority(BaseBuilder $builder, array $priority): BaseBuilder $builder->whereIn('priority', $priority); if ($priority !== ['default']) { + $escapedPriority = array_map([$this->db, 'escape'], $priority); + if ($this->db->DBDriver !== 'MySQLi') { $builder->orderBy( sprintf('CASE %s ', $this->db->protectIdentifiers('priority')) . implode( ' ', - array_map(static fn ($value, $key) => "WHEN '{$value}' THEN {$key}", $priority, array_keys($priority)), + array_map(static fn ($value, $key) => "WHEN {$value} THEN {$key}", $escapedPriority, array_keys($escapedPriority)), ) . ' END', '', @@ -148,10 +150,7 @@ private function setPriority(BaseBuilder $builder, array $priority): BaseBuilder } else { $builder->orderBy( 'FIELD(priority, ' - . implode( - ',', - array_map(static fn ($value) => "'{$value}'", $priority), - ) + . implode(',', $escapedPriority) . ')', '', false, diff --git a/tests/Models/QueueJobModelTest.php b/tests/Models/QueueJobModelTest.php index 4d61f7e..19a882b 100644 --- a/tests/Models/QueueJobModelTest.php +++ b/tests/Models/QueueJobModelTest.php @@ -59,4 +59,25 @@ public function testSkipLockedFalse(): void $this->assertSame($sql, $result); } + + public function testSetPriority(): void + { + $model = model(QueueJobModel::class); + $method = $this->getPrivateMethodInvoker($model, 'setPriority'); + $builder = $model->builder(); + + $result = $method($builder, ['high', 'low']); + + $sql = $result->getCompiledSelect(); + + $this->assertStringContainsString('priority', $sql); + if ($model->db->DBDriver === 'MySQLi') { + $this->assertStringContainsString('FIELD(priority, ', $sql); + } else { + $this->assertStringContainsString('CASE ', $sql); + $this->assertStringContainsString(' WHEN ', $sql); + $this->assertStringContainsString(' THEN ', $sql); + $this->assertStringContainsString(' END', $sql); + } + } } From c4536dab6ac03b900d8b46033fa35ff0bc8706e7 Mon Sep 17 00:00:00 2001 From: Bogdan Date: Fri, 17 Jul 2026 20:19:58 +0200 Subject: [PATCH 2/2] fix: normalize priority keys to prevent unsafe SQL and add relevant tests --- src/Models/QueueJobModel.php | 2 ++ tests/Models/QueueJobModelTest.php | 14 ++++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/Models/QueueJobModel.php b/src/Models/QueueJobModel.php index 1716ac3..56dbbc4 100644 --- a/src/Models/QueueJobModel.php +++ b/src/Models/QueueJobModel.php @@ -131,6 +131,8 @@ private function skipLocked(string $sql): string */ private function setPriority(BaseBuilder $builder, array $priority): BaseBuilder { + $priority = array_values($priority); + $builder->whereIn('priority', $priority); if ($priority !== ['default']) { diff --git a/tests/Models/QueueJobModelTest.php b/tests/Models/QueueJobModelTest.php index 19a882b..72bab08 100644 --- a/tests/Models/QueueJobModelTest.php +++ b/tests/Models/QueueJobModelTest.php @@ -66,17 +66,23 @@ public function testSetPriority(): void $method = $this->getPrivateMethodInvoker($model, 'setPriority'); $builder = $model->builder(); - $result = $method($builder, ['high', 'low']); + $result = $method($builder, ['key1' => 'high', 'key2' => 'low', 'key3' => "un'safe"]); $sql = $result->getCompiledSelect(); $this->assertStringContainsString('priority', $sql); + + $escHigh = $model->db->escape('high'); + $escLow = $model->db->escape('low'); + $escUnsafe = $model->db->escape("un'safe"); + if ($model->db->DBDriver === 'MySQLi') { - $this->assertStringContainsString('FIELD(priority, ', $sql); + $this->assertStringContainsString("FIELD(priority, {$escHigh}, {$escLow}, {$escUnsafe})", $sql); } else { $this->assertStringContainsString('CASE ', $sql); - $this->assertStringContainsString(' WHEN ', $sql); - $this->assertStringContainsString(' THEN ', $sql); + $this->assertStringContainsString("WHEN {$escHigh} THEN 0", $sql); + $this->assertStringContainsString("WHEN {$escLow} THEN 1", $sql); + $this->assertStringContainsString("WHEN {$escUnsafe} THEN 2", $sql); $this->assertStringContainsString(' END', $sql); } }