<mass_assignment_guide>
<title>MASS ASSIGNMENT</title>

<critical>Mass assignment binds client-supplied fields directly into models/DTOs without field-level allowlists. It commonly leads to privilege escalation, ownership changes, and unauthorized state transitions in modern APIs and GraphQL.</critical>

<scope>
- REST/JSON, GraphQL inputs, form-encoded and multipart bodies
- Model binding in controllers/resolvers; ORM create/update helpers
- Writable nested relations, sparse/patch updates, bulk endpoints
</scope>

<methodology>
1. Identify create/update endpoints and GraphQL mutations. Capture full server responses to observe returned fields.
2. Build a candidate list of sensitive attributes per resource: role/isAdmin/permissions, ownerId/accountId/tenantId, status/state, plan/price, limits/quotas, feature flags, verification flags, balance/credits.
3. Inject candidates alongside legitimate updates across transports and encodings; compare before/after state and diffs across roles.
4. Repeat with nested objects, arrays, and alternative shapes (dot/bracket notation, duplicate keys) and in batch operations.
</methodology>

<discovery_techniques>
<surface_map>
- Controllers with automatic binding (e.g., request.json → model); GraphQL input types mirroring models; admin/staff tools exposed via API
- OpenAPI/GraphQL schemas: uncover hidden fields or enums; SDKs often reveal writable fields
- Client bundles and mobile apps: inspect forms and mutation payloads for field names
</surface_map>

<parameter_strategies>
- Flat fields: isAdmin, role, roles[], permissions[], status, plan, tier, premium, verified, emailVerified
- Ownership/tenancy: userId, ownerId, accountId, organizationId, tenantId, workspaceId
- Limits/quotas: usageLimit, seatCount, maxProjects, creditBalance
- Feature flags/gates: features, flags, betaAccess, allowImpersonation
- Billing: price, amount, currency, prorate, nextInvoice, trialEnd
</parameter_strategies>

<shape_variants>
- Alternate shapes: arrays vs scalars; nested JSON; objects under unexpected keys
- Dot/bracket paths: profile.role, profile[role], settings[roles][]
- Duplicate keys and precedence: {"role":"user","role":"admin"}
- Sparse/patch formats: JSON Patch/JSON Merge Patch; try adding forbidden paths or replacing protected fields
</shape_variants>

<encodings_and_channels>
- Content-types: application/json, application/x-www-form-urlencoded, multipart/form-data, text/plain (JSON via server coercion)
- GraphQL: add suspicious fields to input objects; overfetch response to detect changes
- Batch/bulk: arrays of objects; verify per-item allowlists not skipped
</encodings_and_channels>

<exploitation_techniques>
<privilege_escalation>
- Set role/isAdmin/permissions during signup/profile update; toggle admin/staff flags where exposed
</privilege_escalation>

<ownership_takeover>
- Change ownerId/accountId/tenantId to seize resources; move objects across users/tenants
</ownership_takeover>

<feature_gate_bypass>
- Enable premium/beta/feature flags via flags/features fields; raise limits/seatCount/quotas
</feature_gate_bypass>

<billing_and_entitlements>
- Modify plan/price/prorate/trialEnd or creditBalance; bypass server recomputation
</billing_and_entitlements>

<nested_and_relation_writes>
- Writable nested serializers or ORM relations allow creating or linking related objects beyond caller’s scope (e.g., attach to another user’s org)
</nested_and_relation_writes>

<advanced_techniques>
<graphQL_specific>
- Field-level authz missing on input types: attempt forbidden fields in mutation inputs; combine with aliasing/batching to compare effects
- Use fragments to overfetch changed fields immediately after mutation
</graphQL_specific>

<orm_framework_edges>
- Rails: strong parameters misconfig or deep nesting via accepts_nested_attributes_for
- Laravel: $fillable/$guarded misuses; guarded=[] opens all; casts mutating hidden fields
- Django REST Framework: writable nested serializer, read_only/extra_kwargs gaps, partial updates
- Mongoose/Prisma: schema paths not filtered; select:false doesn’t prevent writes; upsert defaults
</orm_framework_edges>

<parser_and_validator_gaps>
- Validators run post-bind and do not cover extra fields; unknown fields silently dropped in response but persisted underneath
- Inconsistent allowlists between mobile/web/gateway; alt encodings bypass validation pipeline
</parser_and_validator_gaps>

<bypass_techniques>
<content_type_switching>
- Switch JSON ↔ form-encoded ↔ multipart ↔ text/plain; some code paths only validate one
</content_type_switching>

<key_path_variants>
- Dot/bracket/object re-shaping to reach nested fields through different binders
</key_path_variants>

<batch_paths>
- Per-item checks skipped in bulk operations; insert a single malicious object within a large batch
</batch_paths>

<race_and_reorder>
- Race two updates: first sets forbidden field, second normalizes; final state may retain forbidden change
</race_and_reorder>

<validation>
1. Show a minimal request where adding a sensitive field changes persisted state for a non-privileged caller.
2. Provide before/after evidence (response body, subsequent GET, or GraphQL query) proving the forbidden attribute value.
3. Demonstrate consistency across at least two encodings or channels.
4. For nested/bulk, show that protected fields are written within child objects or array elements.
5. Quantify impact (e.g., role flip, cross-tenant move, quota increase) and reproducibility.
</validation>

<false_positives>
- Server recomputes derived fields (plan/price/role) ignoring client input
- Fields marked read-only and enforced consistently across encodings
- Only UI-side changes with no persisted effect
</false_positives>

<impact>
- Privilege escalation and admin feature access
- Cross-tenant or cross-account resource takeover
- Financial/billing manipulation and quota abuse
- Policy/approval bypass by toggling verification or status flags
</impact>

<pro_tips>
1. Build a sensitive-field dictionary per resource and fuzz systematically.
2. Always try alternate shapes and encodings; many validators are shape/CT-specific.
3. For GraphQL, diff the resource immediately after mutation; effects are often visible even if the mutation returns filtered fields.
4. Inspect SDKs/mobile apps for hidden field names and nested write examples.
5. Prefer minimal PoCs that prove durable state changes; avoid UI-only effects.
</pro_tips>

<mitigations>
- Enforce server-side allowlists per operation and role; deny unknown fields by default
- Separate input DTOs from domain models; map explicitly
- Recompute derived fields (role/plan/owner) from trusted context; ignore client values
- Lock nested writes to owned resources; validate foreign keys against caller scope
- For GraphQL, use input types that expose only permitted fields and enforce resolver-level checks
</mitigations>

<remember>Mass assignment is eliminated by explicit mapping and per-field authorization. Treat every client-supplied attribute—especially nested or batch inputs—as untrusted until validated against an allowlist and caller scope.</remember>
</mass_assignment_guide>
