Home > Software design >  How do I avoid ActiveRecord::UnknownAttributeReference when calling where in Rails?
How do I avoid ActiveRecord::UnknownAttributeReference when calling where in Rails?

Time:08-12

I have a Rails app with an endpoint receiving a structure containing, among other things, a bunch of numbers:

=> #<ActionController::Parameters {"list"=>[{"item_id"=>"417", "quantity"=>"5"}, {"item_id"=>"418", "quantity"=>"1"}, {"item_id"=>"416", "quantity"=>"2"}], "controller"=>"items", "action"=>"total"} permitted: false>

After doing this stuff with .require and .permit:

  def purchase_list
    params.require(:list).map do |list_entry|
      list_entry.permit(:item_id, :quantity).to_h
    end
  end

I extract the stuff I need and store it in a purchase_list variable from a method call:

[{"item_id"=>"417", "quantity"=>"5"}, {"item_id"=>"418", "quantity"=>"1"}, {"item_id"=>"416", "quantity"=>"2"}]

I then want to query my database for items records with id values matching those item_id numbers in the request params. I'm doing this:

items = Item.where(id: purchase_list.pluck(:item_id))

Then when I try to use items in any way, for example just something like this (real functionality omitted for brevity):

x = Item.first.id
items.pluck(id: x)

It crashes with this error:

     ActiveRecord::UnknownAttributeReference:
       Dangerous query method (method whose arguments are used as raw SQL) called with non-attribute argument(s): {:id=>425}.This method should not be called with user-provided values, such as request parameters or model attributes. Known-safe values can be passed by wrapping them in Arel.sql().

I've been playing around with Arel.sql() to try and make it work some other way, but I can't get it working. It always gives that ActiveRecord::UnknownAttributeReference error.

I've also tried converting them to numbers with to_i and it won't help.

But from what I've read in the Rails guides and other places, if I'm calling where using the "hash conditions" like this where(id: [x,y,z]), that's one of the ways of specifying where conditions for which Rails should automatically sanitise the inputs to guard against MySQL injections and that sort of thing? I thought only calling where with "pure strings" made it vulnerable?

I would be very grateful if someone could explain to me:

  1. Why is Rails giving me that error if I'm using an approach for which Rails should sanitise the input to make it safe?
  2. How can I accomplish what I'm trying to do? with Arel.sql() or any other way?

I'm using versions:

ruby "3.0.3"
gem "rails", "~> 7.0.3", ">= 7.0.3.1"

CodePudding user response:

There must be something you're not showing here. I tried that code locally and it works for me.

Could you try to run this code in Rails console?

pry(main)> params = ActionController::Parameters.new({"list"=>[{"item_id"=>"417", "quantity"=>"5"}, {"item_id"=>"418", "quantity"=>"1"}, {"item_id"=>"416", "quantity"=>"2"}], "controller"=>"items", "action"=>"total"})
=> #<ActionController::Parameters {"list"=>[{"item_id"=>"417", "quantity"=>"5"}, {"item_id"=>"418", "quantity"=>"1"}, {"item_id"=>"416", "quantity"=>"2"}], "controller"=>"items", "action"=>"total"} permitted: false>
pry(main)> purchase_list = params.permit(list: [:item_id, :quantity])[:list]
=> [#<ActionController::Parameters {"item_id"=>"417", "quantity"=>"5"} permitted: true>,
 #<ActionController::Parameters {"item_id"=>"418", "quantity"=>"1"} permitted: true>,
 #<ActionController::Parameters {"item_id"=>"416", "quantity"=>"2"} permitted: true>]
pry(main)> ids = purchase_list.pluck(:item_id)
=> ["417", "418", "416"]
pry(main)> Item.where(id: ids)
  • Related