· via dev.to (home feed)
Cursor and Claude Code repeatedly generate mass-assignment flaws that let users self-promote to admin
According to a dev.to post, update-profile routes written by Cursor and Claude Code often pass raw request bodies to the database, so any logged-in user can grant themselves admin by adding one field.

A developer writing on dev.to reports that asking AI coding assistants such as Cursor or Claude Code to build an ordinary update-profile endpoint routinely produces code vulnerable to mass assignment, tracked as CWE-915 — a bug class that lets any authenticated user rewrite fields they should never control, including their own role.
The author describes a repeatable test: request a simple settings page with name, bio and avatar fields. The editor generates the form, the API route and the database call in under a minute, and all of it works. But when the resulting PATCH request is replayed with a single extra field, "role": "admin", the server accepts it. The route checked authentication and even scoped the write to the requesting user's own record; it simply never restricted which columns could change.
How the bug works
Mass assignment happens when an endpoint feeds the whole request body straight into a database update. In the typical Node and Prisma output, the handler calls prisma.user.update with data: req.body, so the client effectively picks the attributes that get written. According to the post, the same flaw shows up in Mongoose, where findByIdAndUpdate with the raw body still writes any field that exists in the schema, and in Python handlers that loop over the payload calling setattr on the model.
The Express variant carries a companion defect: it returns the freshly updated user object to the browser, password hash included.
The AI's own fix is a denylist
When told that users can set their own role, the assistants typically strip that one field — destructuring role out of the body and passing everything else through. That closes a single door while leaving emailVerified, credits, plan, stripeCustomerId and orgId writable, along with whatever sensitive column is added next quarter. A denylist has to be updated every time the schema changes, and the author argues nobody does that.
Prisma makes things worse because the data object also accepts nested relation writes. A payload that connects the user to another organization's ID can move an attacker into a different tenant without touching role at all.
Why the pattern keeps coming back
The post's explanation: forwarding the entire body is the shortest code that makes the feature work, and it mirrors the CRUD examples that dominate the training data. The bug is also hard to catch in review. The route has authentication and ownership checks, the form sends only a few fields, and every happy-path test passes. TypeScript offers no protection because types vanish at runtime, and Prisma is satisfied as long as a field exists in the schema — which role does.
There is precedent. In 2012 a developer exploited a mass assignment flaw on GitHub to add his own key to the Rails organization and push a commit to the Rails repository; Rails 4 subsequently made strong parameters the default. Express, Fastify, Next.js route handlers and FastAPI ship no equivalent, so the protection exists only if someone writes it — and the AI does not.
The recommended fix
The prescribed remedy is an allowlist schema, around ten lines of zod with .strict() or Pydantic v2 with extra="forbid", that names the only fields a user may change and rejects unknown keys. Only the parsed result should reach the database, admin-only fields belong on separate routes with their own role checks, and responses should use an explicit select so hashes are never returned. The author deliberately rejects unknown keys rather than accepting the libraries' default of silently ignoring them: a rejection turns probing into a visible 4xx in the logs and makes a test fail loudly if the wrong schema is wired to a route.
For existing codebases, the article suggests a quick grep for data: req.body, spread forms of the body inside update calls, and setattr in Python. OWASP's API Security Top 10 (2023) groups the issue under API3, Broken Object Property Level Authorization. The author, who builds a scanning tool that plugs into these editors, concedes a scanner can only flag the pattern — knowing which fields a user may touch is knowledge only the developer has.
Why it matters
AI assistants now generate CRUD plumbing faster than human review can absorb it, and they reproduce the insecure habits embedded in their training material. CWE-915 is trivially exploitable with one extra JSON field yet invisible to auth checks, type systems and normal testing, and it can escalate privileges or move accounts between tenants. Rails solved this at the framework level over a decade ago; the current JavaScript and Python stacks never got a default, so the control has to be written by hand. Teams shipping AI-generated routes should treat a strict allowlist on every write endpoint as table stakes, and audit existing code for raw-body updates before an attacker replays one with a field the form never sent.
- #security
- #ai-code-generation
- #cursor
- #claude-code
- #prisma