I've got a scope in my model :
scope :assigned_to_user, ->(user) {
task_table = UserTask.table_name
joins("INNER JOIN #{task_table}
ON #{task_table}.user_id = #{user.id}
AND (#{task_table}.type_id = #{table_name}.type_id)
AND (#{task_table}.manager_id = #{table_name}.manager_id)
")
}
So after running brakeman report I get this warning :
assigned_to_user | SQL Injection | Possible
So I tried the following :
scope :assigned_to_user, ->(user) {
task_table = UserTask.table_name
joins(ActiveRecord::Base::sanitize("INNER JOIN #{task_table}
ON #{task_table}.user_id = #{user.id}
AND (#{task_table}.type_id = #{table_name}.type_id)
AND (#{task_table}.manager_id = #{table_name}.manager_id)
"))
}
This doesn't work for me because it adds '
(apostrophe) to the front and back of the sql. So when I use this as a part of query which returns some results and I apply this scope it generates the incorrect sql.
I also tried this:
scope :assigned_to_user, ->(user) {
task_table = UserTask.table_name
joins("INNER JOIN #{task_table}
ON #{task_table}.user_id = ?
AND (#{task_table}.type_id = #{table_name}.type_id)
AND (#{task_table}.manager_id = #{table_name}.manager_id)
", user.id)
}
Doesn't even build the statement. And tried couple of other stuff which didn't work and not even worth mentioning. Does anybody have idea how to fix this?
After some kind of research here is what I would use.
There is a method called sanitize_sql_array
(ref), you can use it to escape statements by passing an sql string and replacement values to it like:
sanitize_sql_array(['user_id = :user_id', user_id: 5])
# => "user_id = 5"
If we'd pass a table name to this method it will also escape it, but will apply a quote
method of ActiveRecord::Base.connection
object on a value, which is used to escape variables, but not table names. Maybe sometimes it will work, but it failed for me when I was using PostrgreSQL, because quote
method uses single quotes, but PostgreSQL requires double-quotation for table names.
sanitize_sql_array([
'INNER JOIN :table_name ON :table_name.user_id = :user_id',
{ table_name: 'users', user_id: 5 }
])
# => "INNER JOIN 'users' ON 'users'.user_id = 5"
connection
object also has a method quote_table_name
, which could be separately applied on table names, to make sure that they are escaped + use sanitize_sql_array
for user id.
scope :assigned_to_user, -> (user) {
task_table = connection.quote_table_name(UserTask.table_name)
current_table = connection.quote_table_name(table_name)
sanitized_sql = sanitize_sql_array([
"INNER JOIN #{task_table}
ON #{task_table}.user_id = :user_id
AND (#{task_table}.type_id = #{current_table}.type_id)
AND (#{task_table}.manager_id = #{current_table}.manager_id)",
{ user_id: user.id }
])
joins(sanitized_sql)
}
Or you could actually just use sanitize
on user.id
instead of wrapping everything in sanitize_sql_array
method call (#{sanitize(user.id)}
).
By the way, Brakeman won't already show any warnings, because query has been moved to a variable. Brakeman literally parses your code as is and it does not know about a variable and it's content. So all this thing is just to make yourself sure that everything is being escaped.
Just to shut up Brakeman you could just move a query to a variable:
scope :assigned_to_user, -> (user) {
task_table = UserTask.table_name
query = "INNER JOIN #{task_table}
ON #{task_table}.user_id = #{user.id}
AND (#{task_table}.type_id = #{table_name}.type_id)
AND (#{task_table}.manager_id = #{table_name}.manager_id)"
joins(query)
}
来源:https://stackoverflow.com/questions/27853371/rails-brakeman-warning-of-sql-injection