Mass assignment is when a framework maps request fields onto model attributes automatically. It's convenient, and it means the request controls which columns get written unless you constrain it.
The form has name and email. The request can have anything.
{ "name": "x", "email": "x@y.com", "role": "admin" }
The fix, by ecosystem
Node / Mongoose, pick the fields explicitly:
Or validate into a known shape first:
z.object strips unknown keys by default, which is the behaviour you want.
Rails. Strong parameters:
Never permit!.
Laravel, $fillable on the model, and validate before assigning:
Prefer $fillable to $guarded: with an allowlist, a column added next year is safe by default.
Django. fields on the form or serializer, never __all__:
FastAPI / Pydantic, the request model is the allowlist, which is why this is rarer here. Just don't define a request model containing fields users shouldn't set.
Nested objects
The place it survives a first pass. Permitting an association can pull in more than intended:
price is now client-controlled. Nested attributes need the same scrutiny as top-level ones, and usually less permission.
Update is worse than create
Creating with an extra field is bad. Updating with one is often worse, because update endpoints tend to accept partial payloads and pass them straight through.
Two problems in one: the field allowlist, and the missing ownership check.
Auditing what you already have
Reasonably detectable. create(req.body), update(req.body), permit!, fields = '__all__' and models without $fillable are all recognisable patterns.
What tooling can't tell you is which fields should be assignable, so it flags the shape and you decide. That's a good division of labour. The list is short and the review is quick.