From 0158ac7cbf362bca3de3bc2f04e12a4bf097687e Mon Sep 17 00:00:00 2001 From: michalsn Date: Mon, 13 Jul 2026 19:17:41 +0200 Subject: [PATCH 1/3] chore: harden database queue priority ordering Co-authored-by: Bogdan Lambarski --- src/Models/QueueJobModel.php | 11 ++++++----- tests/Models/QueueJobModelTest.php | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/Models/QueueJobModel.php b/src/Models/QueueJobModel.php index 4e49263..2e13c8b 100644 --- a/src/Models/QueueJobModel.php +++ b/src/Models/QueueJobModel.php @@ -208,15 +208,19 @@ 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']) { + $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', '', @@ -225,10 +229,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 21fdda8..00ef79f 100644 --- a/tests/Models/QueueJobModelTest.php +++ b/tests/Models/QueueJobModelTest.php @@ -114,4 +114,22 @@ public function testGetFromQueueOnSQLite3ClaimsEachPendingJobOnce(): void $this->seeInDatabase('queue_jobs', ['id' => 2, 'status' => Status::RESERVED->value]); $this->seeInDatabase('queue_jobs', ['id' => 3, 'status' => Status::RESERVED->value]); } + + public function testSetPriorityEscapesPriorityValues(): void + { + $model = model(QueueJobModel::class); + $method = $this->getPrivateMethodInvoker($model, 'setPriority'); + $builder = $model->builder(); + + $priority = [ + 'priority_key' => "default' THEN 0 ELSE 1 END --", + 'default', + ]; + + $result = $method($builder, $priority); + $sql = (string) $result->getCompiledSelect(); + + $this->assertStringContainsString($model->db->escape($priority['priority_key']), $sql); + $this->assertStringNotContainsString('priority_key', $sql); + } } From 4df0b9c57c9f6bfc96488155ebafaf78ff414bfa Mon Sep 17 00:00:00 2001 From: michalsn Date: Mon, 13 Jul 2026 19:20:51 +0200 Subject: [PATCH 2/3] fix rector --- tests/Models/QueueJobModelTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Models/QueueJobModelTest.php b/tests/Models/QueueJobModelTest.php index 00ef79f..107bb33 100644 --- a/tests/Models/QueueJobModelTest.php +++ b/tests/Models/QueueJobModelTest.php @@ -129,7 +129,7 @@ public function testSetPriorityEscapesPriorityValues(): void $result = $method($builder, $priority); $sql = (string) $result->getCompiledSelect(); - $this->assertStringContainsString($model->db->escape($priority['priority_key']), $sql); - $this->assertStringNotContainsString('priority_key', $sql); + $this->assertStringContainsString($model->db->escape($priority['priority_key']), (string) $sql); + $this->assertStringNotContainsString('priority_key', (string) $sql); } } From a28aa8da9c09b5a961279ebf0a256a0a79755f61 Mon Sep 17 00:00:00 2001 From: michalsn Date: Thu, 23 Jul 2026 18:43:54 +0200 Subject: [PATCH 3/3] fix Rector --- tests/Models/QueueJobModelTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Models/QueueJobModelTest.php b/tests/Models/QueueJobModelTest.php index 107bb33..00ef79f 100644 --- a/tests/Models/QueueJobModelTest.php +++ b/tests/Models/QueueJobModelTest.php @@ -129,7 +129,7 @@ public function testSetPriorityEscapesPriorityValues(): void $result = $method($builder, $priority); $sql = (string) $result->getCompiledSelect(); - $this->assertStringContainsString($model->db->escape($priority['priority_key']), (string) $sql); - $this->assertStringNotContainsString('priority_key', (string) $sql); + $this->assertStringContainsString($model->db->escape($priority['priority_key']), $sql); + $this->assertStringNotContainsString('priority_key', $sql); } }