diff --git a/system/BaseModel.php b/system/BaseModel.php index cd8b475196d5..5921f2ced7fd 100644 --- a/system/BaseModel.php +++ b/system/BaseModel.php @@ -853,7 +853,7 @@ public function insertBatch(?array $set = null, ?bool $escape = null, int $batch // Must be called first so we don't // strip out created_at values. - $row = $this->doProtectFields($row); + $row = $this->doProtectFieldsForInsert($row); // Set created_at and updated_at with same time $date = $this->setDate(); @@ -1228,10 +1228,10 @@ public function protect(bool $protect = true) } /** - * Ensures that only the fields that are allowed to be updated - * are in the data array. + * Ensures that only the fields that are allowed to be updated are + * in the data array. * - * Used by insert() and update() to protect against mass assignment + * Used by update() and updateBatch() to protect against mass assignment * vulnerabilities. * * @param array $data Data @@ -1257,6 +1257,22 @@ protected function doProtectFields(array $data): array return $data; } + /** + * Ensures that only the fields that are allowed to be inserted are in + * the data array. + * + * Used by insert() and insertBatch() to protect against mass assignment + * vulnerabilities. + * + * @param array $data Data + * + * @throws DataException + */ + protected function doProtectFieldsForInsert(array $data): array + { + return $this->doProtectFields($data); + } + /** * Sets the date or current date if null value is passed. * diff --git a/system/Model.php b/system/Model.php index cc901f831de1..aedc51200445 100644 --- a/system/Model.php +++ b/system/Model.php @@ -734,6 +734,41 @@ public function insert($data = null, bool $returnID = true) return parent::insert($data, $returnID); } + /** + * Ensures that only the fields that are allowed to be inserted are in + * the data array. + * + * Used by insert() and insertBatch() to protect against mass assignment + * vulnerabilities. + * + * @param array $data Data + * + * @throws DataException + */ + protected function doProtectFieldsForInsert(array $data): array + { + if (! $this->protectFields) { + return $data; + } + + if (empty($this->allowedFields)) { + throw DataException::forInvalidAllowedFields(static::class); + } + + foreach (array_keys($data) as $key) { + // Do not remove the non-auto-incrementing primary key data. + if ($this->useAutoIncrement === false && $key === $this->primaryKey) { + continue; + } + + if (! in_array($key, $this->allowedFields, true)) { + unset($data[$key]); + } + } + + return $data; + } + /** * Updates a single record in the database. If an object is provided, * it will attempt to convert it into an array. diff --git a/tests/system/Models/InsertModelTest.php b/tests/system/Models/InsertModelTest.php index 141eebdd6307..f538b417c935 100644 --- a/tests/system/Models/InsertModelTest.php +++ b/tests/system/Models/InsertModelTest.php @@ -65,6 +65,26 @@ public function testInsertBatchSuccess(): void $this->seeInDatabase('job', ['name' => 'Cab Driver']); } + public function testInsertBatchUseAutoIncrementSetToFalse(): void + { + $insertData = [ + [ + 'key' => 'key1', + 'value' => 'value1', + ], + [ + 'key' => 'key2', + 'value' => 'value2', + ], + ]; + + $this->createModel(WithoutAutoIncrementModel::class); + $this->model->insertBatch($insertData); + + $this->seeInDatabase('without_auto_increment', ['key' => 'key1']); + $this->seeInDatabase('without_auto_increment', ['key' => 'key2']); + } + public function testInsertBatchValidationFail(): void { $jobData = [