Repository navigation
fix: guarded could be skipped with a changed key - #40
Conversation
Bot reviewBlocking: none. The fix is correct. Every write path that takes a user map goes through Non-blocking:
Checks (local): |
A key like " is_admin", "IS_ADMIN" or "users.is_admin" set a guarded column. The SQL builder trims the name, and SQLite and MySQL match names without case. So the guarded check, which used the raw key, missed it. Now the guarded check compares the trimmed, lower case name without a table prefix. With only guarded set, a key must also be one of the model columns. A fillable key must still match exactly.
e777126 to
13240c1
Compare
Problem
A model with
guarded => ['is_admin']did not block every key that setsis_admin.These keys got through and set the column:
" is_admin"(space in front)"is_admin\t"(tab at the end)"IS_ADMIN"(upper case)"users.is_admin"(table prefix, a risk on MySQL)The guarded check used the raw map key. But the SQL builder trims the name. SQLite and MySQL also match column names without case.
Effect
A user could send
{"IS_ADMIN": 1}in a request body and make themselves an admin. This was true for a queryupdate(),update()on a loaded model,upsert(),insertOrIgnore()andinsertMultiple().create()was not affected.Fix
Only
_onlyFillableinlib/src/model.dartchanges.guardedset, a key must also be exactly one oftableColumns, like Laravel'sisGuardableColumn. A model with notableColumnsuses only the clean name check.fillablekey must still match the name exactly. A changed key never passesfillable.No public API change. Normal exact keys work as before.
Tests
is_adminstays0andnamestill saves. It fails without the fix.NAME,nameandIS_ADMINdo not pass.dart teston SQLite: 555 passed, 2 skipped.dart analyze --fatal-infosanddart formatare clean. Postgres and MySQL run in CI.