This blog is part of our Rails 5 series.

In older Rails version (< 3.2), when an empty array was passed to a where clause or to a find_by query, it generated SQL with an IS NULL clause.

User.find_by_email([]).to_sql
#=> "SELECT * FROM users WHERE email IS NULL"

User.find_by_email([nil]).to_sql
#=> "SELECT * FROM users WHERE email IS NULL"

Also, when JSON data of the request was parsed and params got generated the deep munging converted empty arrays to nil.

For example, When the following JSON data is posted to a Rails controller

{"property_grouping":{"project_id":289,"name":"test group2","property_ids":[]}}

It gets converted into the following params in the controller.

{"property_grouping"=>{"project_id"=>289, "name"=>"test group2", "property_ids"=>nil, },
"action"=>"...", "controller"=>"...", "format"=>"json"}

This in combination with the fact that Active Record constructs IS NULL query when blank array is passed became one of the security threats and one of the most complained issues in Rails.

The security threat we had was that it was possible for an attacker to issue unexpected database queries with “IS NULL” where clauses. Though there was no threat of an insert being carried out, there could be scope for firing queries that would check for NULL even if it wasn’t intended.

In later version of Rails(> 3.2), we had a different way of handling blank arrays in Active Record find_by and where clauses.

User.find_by_email([]).to_sql
#=> "SELECT "users".* FROM "users" WHERE 1=0 LIMIT 1"

User.find_by_email([nil]).to_sql
#=> "SELECT * FROM users WHERE email IS NULL"

As you can see a conditional for empty array doesn’t trigger IS NULL query, which solved part of the problem.

We still had conversion of empty array to nil in the deep munging in place and hence there was still a threat of undesired behavior when request contained empty array.

One way to handle it was to add before_action hooks to the action that could modify the value to empty array if it were nil.

In Rails 5, empty array does not get converted to nil in deep munging. With this change, the empty array will persist as is from request to the params in the controller.