mer10z_tech
mer10z_tech

Reputation: 695

rails dynamic where sql query

I have an object with a bunch of attributes that represent searchable model attributes, and I would like to dynamically create an sql query using only the attributes that are set. I created the method below, but I believe it is susceptible to sql injection attacks. I did some research and read over the rails active record query interface guide, but it seems like the where condition always needs a statically defined string as the first parameter. I also tried to find a way to sanitize the sql string produced by my method, but it doesn't seem like there is a good way to do that either.

How can I do this better? Should I use a where condition or just somehow sanitize this sql string? Thanks.

def query_string
  to_return = ""

  self.instance_values.symbolize_keys.each do |attr_name, attr_value|
    if defined?(attr_value) and !attr_value.blank?
      to_return << "#{attr_name} LIKE '%#{attr_value}%' and "
    end
  end
  to_return.chomp(" and ")
end

Upvotes: 1

Views: 3036

Answers (1)

mu is too short
mu is too short

Reputation: 434606

Your approach is a little off as you're trying to solve the wrong problem. You're trying to build a string to hand to ActiveRecord so that it can build a query when you should simply be trying to build a query.

When you say something like:

Model.where('a and b')

that's the same as saying:

Model.where('a').where('b')

and you can say:

Model.where('c like ?', pattern)

instead of:

Model.where("c like '#{pattern}'")

Combining those two ideas with your self.instance_values you could get something like:

def query
  self.instance_values.select { |_, v| v.present? }.inject(YourModel) do |q, (name, value)|
    q.where("#{name} like ?", "%#{value}%")
  end
end

or even:

def query
  empties      = ->(_, v) { v.blank? }
  add_to_query = ->(q, (n, v)) { q.where("#{n} like ?", "%#{v}%") }
  instance_values.reject(&empties)
                 .inject(YourModel, &add_to_query)
end

Those assume that you've properly whitelisted all your instance variables. If you haven't then you should.

Upvotes: 2

Related Questions