Block Mass Assignment and Unscoped Finds in Rails

Catches params.permit!, permitted role or ownership attributes, and Model.find(params[:id]) lookups that skip current_user or tenant scoping.

What it flags

Flaggedapp/controllers/documents_controller.rb
class DocumentsController < ApplicationController
  before_action :authenticate_user!

  def update
    document = Document.find(params[:id]) # any signed-in user can update any document
    document.update!(document_params)
    render json: document
  end

  private

  def document_params
    params.expect(document: [:title, :body, :workspace_id, :owner_id])
  end
end
Passesapp/controllers/documents_controller.rb
class DocumentsController < ApplicationController
  before_action :authenticate_user!

  def update
    document = current_user.documents.find(params[:id])
    document.update!(document_params)
    render json: document
  end

  private

  def document_params
    params.expect(document: [:title, :body])
  end
end

Why it matters

Strong parameters reject unfiltered hashes, and the developer still decides which attributes are safe to permit. Permitting owner_id or role lets any user reassign records or promote themselves, and Model.find(params[:id]) returns any row whose id a user can guess or enumerate. Both look like ordinary controller code, and request specs usually sign in as the record's owner, so the suite passes.

Agent instructions

cubic checks every pull request against these instructions. Paste them into AGENTS.md or Cursor rules and your AI coding tool follows them while it writes code.

AGENTS.md

Add this section to AGENTS.md at the repository root. Codex, Cursor, and other AI coding tools read it.

## Block Mass Assignment and Unscoped Finds in Rails

Applies to `**/app/controllers/**/*.rb`, `**/app/models/**/*.rb`, `**/app/policies/**/*.rb`.

Review Rails controllers and strong parameters for mass assignment and record lookups that skip ownership or tenant checks.

Flag changes that:
- Call `params.permit!` or `to_unsafe_h` and pass the result to `new`, `create`, `update`, or `assign_attributes`.
- Permit attributes that grant privileges or move ownership, such as `role`, `admin`, `account_id`, `user_id`, `owner_id`, or `organization_id`, in `permit` or `expect` calls for controllers ordinary users can reach.
- Load a record by a user-supplied id with `Model.find`, `find_by`, or `where(id: ...)` instead of through `current_user`, the current tenant, or a policy scope.
- Scope through a parent looked up from params, such as `Account.find(params[:account_id]).projects`, without checking that the current user belongs to that parent.
- Accept a foreign key such as `project_id` from params when creating a record without checking that the referenced record belongs to the current user or tenant.
- Remove or skip an authorization call on a changed action, such as Pundit's `authorize` or `policy_scope`, or CanCanCan's `load_and_authorize_resource`.

Do not flag:
- Actions that run an authorization check (`authorize`, `policy_scope`, `accessible_by`, `load_and_authorize_resource`) on the record before using or returning it.
- Admin controllers behind an admin-only `before_action` that intentionally edit roles or ownership.
- Records that are public by design, such as published articles looked up by slug.

Checks files matching **/app/controllers/**/*.rb, **/app/models/**/*.rb, **/app/policies/**/*.rb.

More Ruby on Rails agents

See all Ruby on Rails code review standards

Run this agent on every pull request

cubic reviews every pull request against the agents you turn on and comments on the lines that break them.

Catch this in your code