From f602fae26164e4854ade6e166738dacee6d604e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=AE=E3=81=B6?= Date: Tue, 3 Mar 2026 10:55:56 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20SQL=E8=AD=98=E5=88=A5=E5=AD=90=E3=81=AE?= =?UTF-8?q?=E3=82=A8=E3=82=B9=E3=82=B1=E3=83=BC=E3=83=97=E5=87=A6=E7=90=86?= =?UTF-8?q?=E3=82=92quoteIdentifier=E3=81=AB=E7=B5=B1=E4=B8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit テーブル名をSQL文に直接結合していた箇所をDBALの quoteIdentifier()に統一し、識別子インジェクションを防止。 - DataMigrationService: validateTableName()追加、resetTable()/ setIdSeq()/begin()でquoteIdentifier使用 - setIdSeq(): シーケンス名・値をプリペアドステートメントに変更 - ConfigController: saveStock()/saveProductImage()/saveOrderItem()/ upsertMasterFromCsv()/fix4x()でquoteIdentifier使用 Co-Authored-By: Claude Opus 4.6 --- Controller/Admin/ConfigController.php | 10 ++++---- Service/DataMigrationService.php | 34 +++++++++++++++------------ 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/Controller/Admin/ConfigController.php b/Controller/Admin/ConfigController.php index 33f2872..e6b96ee 100644 --- a/Controller/Admin/ConfigController.php +++ b/Controller/Admin/ConfigController.php @@ -966,7 +966,7 @@ private function upsertMasterFromCsv($em, $dir, $csvName, $tableName, array $opt $updateSql = implode(', ', array_map(function ($c) { return '"' . $c . '" = EXCLUDED."' . $c . '"'; }, $updateCols)); - $sql = 'INSERT INTO ' . $tableName . ' (' . $colsSql . ') VALUES (' . $placeholders . ') ON CONFLICT (id) DO UPDATE SET ' . $updateSql; + $sql = 'INSERT INTO ' . $em->quoteIdentifier($tableName) . ' (' . $colsSql . ') VALUES (' . $placeholders . ') ON CONFLICT (id) DO UPDATE SET ' . $updateSql; $em->prepare($sql)->executeStatement(array_values($insertValues)); $rowCount++; } @@ -1397,7 +1397,7 @@ private function saveStock($em) $builder = new BulkInsertQuery($em, $tableName); $builder->setColumns($listTableColumns); - $em->exec('DELETE FROM ' . $tableName); + $em->exec('DELETE FROM ' . $em->quoteIdentifier($tableName)); $i = 1; $batchSize = 20; @@ -1436,7 +1436,7 @@ private function saveProductImage($em) $builder = new BulkInsertQuery($em, $tableName); $builder->setColumns($listTableColumns); - $em->exec('DELETE FROM ' . $tableName); + $em->exec('DELETE FROM ' . $em->quoteIdentifier($tableName)); $i = 1; $batchSize = 20; @@ -2037,7 +2037,7 @@ private function saveOrderItem($em) $builder = new BulkInsertQuery($em, $tableName); $builder->setColumns($listTableColumns); - $i = $em->fetchOne('SELECT max(id) + 1 FROM ' . $tableName); + $i = $em->fetchOne('SELECT max(id) + 1 FROM ' . $em->quoteIdentifier($tableName)); $batchSize = 20; foreach ($this->order_item as $order_id => $type) { foreach ($type as $key => $value) { @@ -2296,7 +2296,7 @@ private function fix4x($em, $tmpDir, $csvName) $updateSet = array_map(fn($c) => '"' . $c . '" = EXCLUDED."' . $c . '"', $updateCols); $conflictCols = array_map(fn($c) => '"' . $c . '"', $primaryKeys); - $sql = 'INSERT INTO "' . $tableName . '" (' . implode(', ', $cols) . ') ' . + $sql = 'INSERT INTO ' . $em->quoteIdentifier($tableName) . ' (' . implode(', ', $cols) . ') ' . 'VALUES (' . implode(', ', $placeholders) . ') ' . 'ON CONFLICT (' . implode(', ', $conflictCols) . ') ' . 'DO UPDATE SET ' . implode(', ', $updateSet); diff --git a/Service/DataMigrationService.php b/Service/DataMigrationService.php index 8493e03..67e4f8b 100644 --- a/Service/DataMigrationService.php +++ b/Service/DataMigrationService.php @@ -136,21 +136,23 @@ public function updateEnv($newMagicValue) file_put_contents($envFile, $env); } - public function resetTable(Connection $em, $tableName) + /** + * テーブル名のバリデーション + */ + private function validateTableName(string $tableName): void { - $platform = $em->getDatabasePlatform()->getName(); - - if ($platform == 'mysql') { - $em->exec('DELETE FROM ' . $tableName); - } elseif ($platform == 'postgresql') { - // PostgreSQLでは fix4x() はUPSERTを使うため、このメソッドは呼ばれない - // saveToC() などから呼ばれる場合はDELETEを実行 - $em->exec('DELETE FROM "' . $tableName . '"'); - } else { - $em->exec('DELETE FROM ' . $tableName); + if (!preg_match('/^[a-zA-Z_][a-zA-Z0-9_]{0,63}$/', $tableName)) { + throw new \InvalidArgumentException('Invalid table name: ' . $tableName); } } + public function resetTable(Connection $em, $tableName) + { + $this->validateTableName($tableName); + $quoted = $em->quoteIdentifier($tableName); + $em->exec('DELETE FROM ' . $quoted); + } + public function convertNULL($data) { foreach ($data as &$v) { @@ -305,7 +307,7 @@ public function begin($em, $context = NULL) foreach ($targetTables as $t) { $exists = $em->fetchOne("SELECT 1 FROM pg_tables WHERE schemaname='public' AND tablename=?", [$t]); if ($exists) { - $existing[] = '"' . str_replace('"', '""', $t) . '"'; + $existing[] = $em->quoteIdentifier($t); } } if ($existing) { @@ -324,11 +326,13 @@ public function begin($em, $context = NULL) public function setIdSeq($em, $tableName) { - $max = $em->fetchOne('SELECT coalesce(max(id), 0) + 1 FROM ' . $tableName); + $this->validateTableName($tableName); + $quoted = $em->quoteIdentifier($tableName); + $max = $em->fetchOne('SELECT coalesce(max(id), 0) + 1 FROM ' . $quoted); $seq = $tableName . '_id_seq'; - $count = $em->fetchOne("select count(*) from pg_class where relname = '$seq';"); + $count = $em->fetchOne("select count(*) from pg_class where relname = ?", [$seq]); if ($count) { - $em->exec("SELECT setval('$seq', $max);"); + $em->executeStatement("SELECT setval(?, ?)", [$seq, $max]); } }