From 4d0981b1bff770a107aef3aa6345a33cfed1b83e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?=
 <rafaelmfranca@gmail.com>
Date: Wed, 6 Aug 2014 21:17:19 -0300
Subject: [PATCH] Check attributes passed to create_with and where

If the request parameters are passed to create_with and where they can
be used to do mass assignment when used in combination with
Relation#create.

Fixes CVE-2014-3514
---
 .../forbidden_attributes_protection.rb             |  1 +
 .../lib/active_record/relation/query_methods.rb    | 16 ++++++++++--
 .../cases/forbidden_attributes_protection_test.rb  | 30 ++++++++++++++++++++++
 3 files changed, 45 insertions(+), 2 deletions(-)

diff --git a/activemodel/lib/active_model/forbidden_attributes_protection.rb b/activemodel/lib/active_model/forbidden_attributes_protection.rb
index 7468f95..b4fa378 100644
--- a/activemodel/lib/active_model/forbidden_attributes_protection.rb
+++ b/activemodel/lib/active_model/forbidden_attributes_protection.rb
@@ -23,5 +23,6 @@ module ActiveModel
           attributes
         end
       end
+      alias :sanitize_forbidden_attributes :sanitize_for_mass_assignment
   end
 end
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 72d3917..acf2f35 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -1,9 +1,12 @@
 require 'active_support/core_ext/array/wrap'
+require 'active_model/forbidden_attributes_protection'
 
 module ActiveRecord
   module QueryMethods
     extend ActiveSupport::Concern
 
+    include ActiveModel::ForbiddenAttributesProtection
+
     # WhereChain objects act as placeholder for queries in which #where does not have any parameter.
     # In this case, #where must be chained with #not to return a new relation.
     class WhereChain
@@ -561,7 +564,10 @@ module ActiveRecord
       if opts == :chain
         WhereChain.new(self)
       else
-        references!(PredicateBuilder.references(opts)) if Hash === opts
+        if Hash === opts
+          opts = sanitize_forbidden_attributes(opts)
+          references!(PredicateBuilder.references(opts))
+        end
 
         self.where_values += build_where(opts, rest)
         self
@@ -711,7 +717,13 @@ module ActiveRecord
     end
 
     def create_with!(value) # :nodoc:
-      self.create_with_value = value ? create_with_value.merge(value) : {}
+      if value
+        value = sanitize_forbidden_attributes(value)
+        self.create_with_value = create_with_value.merge(value)
+      else
+        self.create_with_value = {}
+      end
+
       self
     end
 
diff --git a/activerecord/test/cases/forbidden_attributes_protection_test.rb b/activerecord/test/cases/forbidden_attributes_protection_test.rb
index 981a75f..f4e7646 100644
--- a/activerecord/test/cases/forbidden_attributes_protection_test.rb
+++ b/activerecord/test/cases/forbidden_attributes_protection_test.rb
@@ -66,4 +66,34 @@ class ForbiddenAttributesProtectionTest < ActiveRecord::TestCase
     person = Person.new
     assert_nil person.assign_attributes(ProtectedParams.new({}))
   end
+
+  def test_create_with_checks_permitted
+    params = ProtectedParams.new(first_name: 'Guille', gender: 'm')
+
+    assert_raises(ActiveModel::ForbiddenAttributesError) do
+      Person.create_with(params).create!
+    end
+  end
+
+  def test_create_with_works_with_params_values
+    params = ProtectedParams.new(first_name: 'Guille')
+
+    person = Person.create_with(first_name: params[:first_name]).create!
+    assert_equal 'Guille', person.first_name
+  end
+
+  def test_where_checks_permitted
+    params = ProtectedParams.new(first_name: 'Guille', gender: 'm')
+
+    assert_raises(ActiveModel::ForbiddenAttributesError) do
+      Person.where(params).create!
+    end
+  end
+
+  def test_where_works_with_params_values
+    params = ProtectedParams.new(first_name: 'Guille')
+
+    person = Person.where(first_name: params[:first_name]).create!
+    assert_equal 'Guille', person.first_name
+  end
 end
-- 
2.0.0

