acc-check-mass-assignment
SKILL.md
Mass Assignment Security Check (A01:2021)
Analyze PHP code for mass assignment vulnerabilities where user input directly populates model attributes.
Detection Patterns
1. Request::all() Passed to Create/Update
// CRITICAL: All request data used to create model
class UserController
{
public function store(Request $request): Response
{
$user = User::create($request->all());
// Attacker can set: is_admin=true, role=superadmin, balance=999999
return new Response($user);
}
}
// CRITICAL: All input to update
class ProfileController
{
public function update(Request $request, int $id): Response
{
$user = User::findOrFail($id);
$user->update($request->all());
// Attacker modifies fields not in the form
}
}
// CORRECT: Whitelist specific fields
class UserController
{
public function store(Request $request): Response
{
$user = User::create($request->only(['name', 'email', 'password']));
return new Response($user);
}
}
2. Missing $fillable or $guarded (Laravel)
// CRITICAL: No mass assignment protection
class User extends Model
{
// No $fillable defined β all fields assignable!
// No $guarded defined β nothing protected!
}
// VULNERABLE: Empty guarded (allows everything)
class User extends Model
{
protected $guarded = []; // Disables ALL protection!
}
// CORRECT: Explicit fillable
class User extends Model
{
protected $fillable = ['name', 'email', 'password'];
// is_admin, role, etc. are NOT fillable
}
3. Array Spread / Merge Into Entity
// CRITICAL: Unfiltered array to entity constructor
class CreateUserUseCase
{
public function execute(array $data): User
{
return new User(...$data); // Any key becomes a constructor argument!
}
}
// CRITICAL: Array merge with user input
class UpdateHandler
{
public function handle(UpdateCommand $command): void
{
$entity = $this->repo->find($command->id());
$merged = array_merge($entity->toArray(), $command->data());
// User-supplied data overrides ALL fields
$this->repo->save(User::fromArray($merged));
}
}
// CORRECT: Explicit mapping
class CreateUserUseCase
{
public function execute(CreateUserCommand $command): User
{
return new User(
name: $command->name(),
email: $command->email(),
// Only mapped fields, no mass assignment
);
}
}
4. Doctrine Entity Public Setters
// VULNERABLE: All setters exposed, bulk update possible
class User
{
public function setRole(string $role): void { $this->role = $role; }
public function setIsAdmin(bool $isAdmin): void { $this->isAdmin = $isAdmin; }
public function setBalance(int $balance): void { $this->balance = $balance; }
}
// Combined with:
foreach ($request->all() as $key => $value) {
$setter = 'set' . ucfirst($key);
if (method_exists($user, $setter)) {
$user->$setter($value); // Mass assignment via reflection!
}
}
5. API Resource Without Field Filtering
// VULNERABLE: PATCH endpoint accepts any field
class UserApiController
{
public function patch(Request $request, int $id): Response
{
$user = $this->repo->find($id);
$data = json_decode($request->getContent(), true);
foreach ($data as $field => $value) {
$user->{'set' . ucfirst($field)}($value); // Any field!
}
}
}
// CORRECT: Explicit allowed fields for PATCH
class UserApiController
{
private const array PATCHABLE_FIELDS = ['name', 'email', 'phone'];
public function patch(Request $request, int $id): Response
{
$user = $this->repo->find($id);
$data = json_decode($request->getContent(), true);
foreach ($data as $field => $value) {
if (!in_array($field, self::PATCHABLE_FIELDS, true)) {
throw new BadRequestException("Field '$field' is not modifiable");
}
}
}
}
Grep Patterns
# Request::all() to create/update
Grep: "::create\(\\\$request->all\(\)\)|->update\(\\\$request->all\(\)\)" --glob "**/*.php"
Grep: "request->all\(\)" --glob "**/*Controller*.php"
# Missing fillable/guarded (Laravel)
Grep: "extends Model" --glob "**/*.php"
Grep: "\\\$fillable|\\\$guarded" --glob "**/*.php"
# Empty guarded
Grep: "\\\$guarded = \[\]" --glob "**/*.php"
# Array spread to constructor
Grep: "new.*\(\.\.\.\\\$" --glob "**/*.php"
# Dynamic setters
Grep: "->.*\\\$.*\(|method_exists.*set" --glob "**/*.php"
# array_merge with user input
Grep: "array_merge\(.*request|array_merge\(.*\\\$data" --glob "**/*.php"
Severity Classification
| Pattern | Severity |
|---|---|
| Request::all() to create/update | π΄ Critical |
| Empty $guarded = [] | π΄ Critical |
| Dynamic setters from user input | π΄ Critical |
| Missing $fillable on Model | π Major |
| Array spread from unvalidated input | π Major |
| No field whitelist on PATCH | π‘ Minor |
Output Format
### Mass Assignment: [Description]
**Severity:** π΄/π /π‘
**Location:** `file.php:line`
**CWE:** CWE-915 (Improperly Controlled Modification of Dynamically-Determined Object Attributes)
**OWASP:** A01:2021 β Broken Access Control
**Issue:**
[Description of the mass assignment vulnerability]
**Attack Vector:**
Attacker adds `is_admin=true` to request body, gaining admin privileges.
**Code:**
```php
// Vulnerable code
Fix:
// With explicit field whitelisting
Weekly Installs
1
Repository
dykyi-roman/aweβ¦ude-codeGitHub Stars
39
First Seen
Feb 11, 2026
Security Audits
Installed on
opencode1
claude-code1