Pārlūkot izejas kodu

Fix R01-N10: bind sprint_id with placeholder in MAX(sort_order) lookups

Three repo-level read paths previously interpolated an integer route
parameter directly into SQL ('... WHERE sprint_id = ' . $sprintId).
Today the value is always int-cast at the router boundary, so this is
not exploitable, but the contract is implicit — one careless future
caller and the repo accepts an unvalidated string.

Switched all three to prepared statements with `?` placeholders:
- TaskRepository::create (line 60)
- TaskRepository::moveToSprint (line 100)
- SprintWorkerRepository::add (line 56)

Mechanical change; behaviour is identical.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
chiappa 2 dienas atpakaļ
vecāks
revīzija
c1dbfc1ad4

+ 5 - 3
src/Repositories/SprintWorkerRepository.php

@@ -52,9 +52,11 @@ final class SprintWorkerRepository
      */
     public function add(int $sprintId, int $workerId, float $rtb): SprintWorker
     {
-        $maxOrder = (int) $this->pdo
-            ->query('SELECT COALESCE(MAX(sort_order), 0) FROM sprint_workers WHERE sprint_id = ' . $sprintId)
-            ->fetchColumn();
+        $maxStmt = $this->pdo->prepare(
+            'SELECT COALESCE(MAX(sort_order), 0) FROM sprint_workers WHERE sprint_id = ?'
+        );
+        $maxStmt->execute([$sprintId]);
+        $maxOrder = (int) $maxStmt->fetchColumn();
         $newOrder = $maxOrder + 1;
 
         $stmt = $this->pdo->prepare(

+ 10 - 6
src/Repositories/TaskRepository.php

@@ -56,9 +56,11 @@ final class TaskRepository
         ?int $linkedTaskId = null,
     ): Task {
         $now = gmdate('Y-m-d\TH:i:s\Z');
-        $max = (int) $this->pdo
-            ->query('SELECT COALESCE(MAX(sort_order), 0) FROM tasks WHERE sprint_id = ' . $sprintId)
-            ->fetchColumn();
+        $maxStmt = $this->pdo->prepare(
+            'SELECT COALESCE(MAX(sort_order), 0) FROM tasks WHERE sprint_id = ?'
+        );
+        $maxStmt->execute([$sprintId]);
+        $max = (int) $maxStmt->fetchColumn();
         $stmt = $this->pdo->prepare(
             'INSERT INTO tasks
                 (sprint_id, title, owner_worker_id, priority, sort_order,
@@ -96,9 +98,11 @@ final class TaskRepository
         $this->pdo->prepare('DELETE FROM task_assignments WHERE task_id = ?')
             ->execute([$taskId]);
 
-        $maxOrder = (int) $this->pdo
-            ->query('SELECT COALESCE(MAX(sort_order), 0) FROM tasks WHERE sprint_id = ' . $destSprintId)
-            ->fetchColumn();
+        $maxStmt = $this->pdo->prepare(
+            'SELECT COALESCE(MAX(sort_order), 0) FROM tasks WHERE sprint_id = ?'
+        );
+        $maxStmt->execute([$destSprintId]);
+        $maxOrder = (int) $maxStmt->fetchColumn();
 
         $stmt = $this->pdo->prepare(
             'UPDATE tasks