2017-07-09 13:41:28 -04:00
# frozen_string_literal: true
2011-06-06 14:17:44 -04:00
require " cases/helper "
2016-08-06 12:26:20 -04:00
require " models/binary "
require " models/author "
require " models/post "
2018-01-29 07:28:42 -05:00
require " models/customer "
2008-09-11 16:38:20 -04:00
class SanitizeTest < ActiveRecord :: TestCase
def setup
end
def test_sanitize_sql_array_handles_string_interpolation
quoted_bambi = ActiveRecord :: Base . connection . quote_string ( " Bambi " )
2017-12-10 21:41:27 -05:00
assert_equal " name=' #{ quoted_bambi } ' " , Binary . sanitize_sql_array ( [ " name='%s' " , " Bambi " ] )
assert_equal " name=' #{ quoted_bambi } ' " , Binary . sanitize_sql_array ( [ " name='%s' " , " Bambi " . mb_chars ] )
2008-09-11 16:38:20 -04:00
quoted_bambi_and_thumper = ActiveRecord :: Base . connection . quote_string ( " Bambi \n and \n Thumper " )
2017-12-10 21:41:27 -05:00
assert_equal " name=' #{ quoted_bambi_and_thumper } ' " , Binary . sanitize_sql_array ( [ " name='%s' " , " Bambi \n and \n Thumper " ] )
assert_equal " name=' #{ quoted_bambi_and_thumper } ' " , Binary . sanitize_sql_array ( [ " name='%s' " , " Bambi \n and \n Thumper " . mb_chars ] )
2008-09-11 16:38:20 -04:00
end
def test_sanitize_sql_array_handles_bind_variables
quoted_bambi = ActiveRecord :: Base . connection . quote ( " Bambi " )
2017-12-10 21:41:27 -05:00
assert_equal " name= #{ quoted_bambi } " , Binary . sanitize_sql_array ( [ " name=? " , " Bambi " ] )
assert_equal " name= #{ quoted_bambi } " , Binary . sanitize_sql_array ( [ " name=? " , " Bambi " . mb_chars ] )
2008-09-11 16:38:20 -04:00
quoted_bambi_and_thumper = ActiveRecord :: Base . connection . quote ( " Bambi \n and \n Thumper " )
2017-12-10 21:41:27 -05:00
assert_equal " name= #{ quoted_bambi_and_thumper } " , Binary . sanitize_sql_array ( [ " name=? " , " Bambi \n and \n Thumper " ] )
assert_equal " name= #{ quoted_bambi_and_thumper } " , Binary . sanitize_sql_array ( [ " name=? " , " Bambi \n and \n Thumper " . mb_chars ] )
2008-09-11 16:38:20 -04:00
end
2013-10-12 07:38:37 -04:00
2015-11-07 04:42:28 -05:00
def test_sanitize_sql_array_handles_named_bind_variables
quoted_bambi = ActiveRecord :: Base . connection . quote ( " Bambi " )
2017-12-10 21:41:27 -05:00
assert_equal " name= #{ quoted_bambi } " , Binary . sanitize_sql_array ( [ " name=:name " , name : " Bambi " ] )
assert_equal " name= #{ quoted_bambi } AND id=1 " , Binary . sanitize_sql_array ( [ " name=:name AND id=:id " , name : " Bambi " , id : 1 ] )
2015-11-07 04:42:28 -05:00
quoted_bambi_and_thumper = ActiveRecord :: Base . connection . quote ( " Bambi \n and \n Thumper " )
2017-12-10 21:41:27 -05:00
assert_equal " name= #{ quoted_bambi_and_thumper } " , Binary . sanitize_sql_array ( [ " name=:name " , name : " Bambi \n and \n Thumper " ] )
assert_equal " name= #{ quoted_bambi_and_thumper } AND name2= #{ quoted_bambi_and_thumper } " , Binary . sanitize_sql_array ( [ " name=:name AND name2=:name " , name : " Bambi \n and \n Thumper " ] )
2015-11-07 04:42:28 -05:00
end
2013-10-12 07:38:37 -04:00
def test_sanitize_sql_array_handles_relations
2016-08-06 12:26:20 -04:00
david = Author . create! ( name : " David " )
2013-10-18 16:26:39 -04:00
david_posts = david . posts . select ( :id )
sub_query_pattern = / \ ( \ bselect \ b.*? \ bwhere \ b.*? \ ) /i
2017-12-10 21:41:27 -05:00
select_author_sql = Post . sanitize_sql_array ( [ " id in (?) " , david_posts ] )
2016-08-06 12:26:20 -04:00
assert_match ( sub_query_pattern , select_author_sql , " should sanitize `Relation` as subquery for bind variables " )
2013-10-18 16:26:39 -04:00
2017-12-10 21:41:27 -05:00
select_author_sql = Post . sanitize_sql_array ( [ " id in (:post_ids) " , post_ids : david_posts ] )
2016-08-06 12:26:20 -04:00
assert_match ( sub_query_pattern , select_author_sql , " should sanitize `Relation` as subquery for named bind variables " )
2013-10-12 07:38:37 -04:00
end
2014-02-09 07:05:42 -05:00
def test_sanitize_sql_array_handles_empty_statement
2017-12-10 21:41:27 -05:00
select_author_sql = Post . sanitize_sql_array ( [ " " ] )
2016-08-06 12:26:20 -04:00
assert_equal ( " " , select_author_sql )
2014-02-09 07:05:42 -05:00
end
2014-02-27 13:34:21 -05:00
def test_sanitize_sql_like
2017-12-10 21:41:27 -05:00
assert_equal '100\%' , Binary . sanitize_sql_like ( " 100% " )
assert_equal 'snake\_cased\_string' , Binary . sanitize_sql_like ( " snake_cased_string " )
assert_equal 'C:\\\\Programs\\\\MsPaint' , Binary . sanitize_sql_like ( 'C:\\Programs\\MsPaint' )
assert_equal " normal string 42 " , Binary . sanitize_sql_like ( " normal string 42 " )
2014-02-27 13:34:21 -05:00
end
def test_sanitize_sql_like_with_custom_escape_character
2017-12-10 21:41:27 -05:00
assert_equal " 100!% " , Binary . sanitize_sql_like ( " 100% " , " ! " )
assert_equal " snake!_cased!_string " , Binary . sanitize_sql_like ( " snake_cased_string " , " ! " )
assert_equal " great!! " , Binary . sanitize_sql_like ( " great! " , " ! " )
assert_equal 'C:\\Programs\\MsPaint' , Binary . sanitize_sql_like ( 'C:\\Programs\\MsPaint' , " ! " )
assert_equal " normal string 42 " , Binary . sanitize_sql_like ( " normal string 42 " , " ! " )
2014-02-27 13:34:21 -05:00
end
def test_sanitize_sql_like_example_use_case
searchable_post = Class . new ( Post ) do
2017-12-22 09:49:34 -05:00
def self . search_as_method ( term )
2016-08-06 12:26:20 -04:00
where ( " title LIKE ? " , sanitize_sql_like ( term , " ! " ) )
2014-02-27 13:34:21 -05:00
end
2017-12-22 09:49:34 -05:00
scope :search_as_scope , - > ( term ) {
where ( " title LIKE ? " , sanitize_sql_like ( term , " ! " ) )
}
end
assert_sql ( / LIKE '20!% !_reduction!_!!' / ) do
searchable_post . search_as_method ( " 20% _reduction_! " ) . to_a
2014-02-27 13:34:21 -05:00
end
2014-04-18 13:45:42 -04:00
assert_sql ( / LIKE '20!% !_reduction!_!!' / ) do
2017-12-22 09:49:34 -05:00
searchable_post . search_as_scope ( " 20% _reduction_! " ) . to_a
2014-02-27 13:34:21 -05:00
end
end
2015-11-20 21:34:36 -05:00
def test_bind_arity
2016-08-06 12:26:20 -04:00
assert_nothing_raised { bind " " }
assert_raise ( ActiveRecord :: PreparedStatementInvalid ) { bind " " , 1 }
2015-11-20 21:34:36 -05:00
2016-08-06 12:26:20 -04:00
assert_raise ( ActiveRecord :: PreparedStatementInvalid ) { bind " ? " }
assert_nothing_raised { bind " ? " , 1 }
assert_raise ( ActiveRecord :: PreparedStatementInvalid ) { bind " ? " , 1 , 1 }
2015-11-20 21:34:36 -05:00
end
def test_named_bind_variables
2016-08-06 13:37:57 -04:00
assert_equal " 1 " , bind ( " :a " , a : 1 ) # ' ruby-mode
assert_equal " 1 1 " , bind ( " :a :a " , a : 1 ) # ' ruby-mode
2015-11-20 21:34:36 -05:00
2016-08-06 13:37:57 -04:00
assert_nothing_raised { bind ( " '+00:00' " , foo : " bar " ) }
2015-11-20 21:34:36 -05:00
end
def test_named_bind_arity
2016-08-06 13:44:11 -04:00
assert_nothing_raised { bind " name = :name " , name : " 37signals " }
assert_nothing_raised { bind " name = :name " , name : " 37signals " , id : 1 }
assert_raise ( ActiveRecord :: PreparedStatementInvalid ) { bind " name = :name " , id : 1 }
2015-11-20 21:34:36 -05:00
end
class SimpleEnumerable
include Enumerable
def initialize ( ary )
@ary = ary
end
def each ( & b )
@ary . each ( & b )
end
end
def test_bind_enumerable
quoted_abc = %( #{ ActiveRecord :: Base . connection . quote ( 'a' ) } , #{ ActiveRecord :: Base . connection . quote ( 'b' ) } , #{ ActiveRecord :: Base . connection . quote ( 'c' ) } )
2016-08-06 12:26:20 -04:00
assert_equal " 1,2,3 " , bind ( " ? " , [ 1 , 2 , 3 ] )
assert_equal quoted_abc , bind ( " ? " , %w( a b c ) )
2015-11-20 21:34:36 -05:00
2016-08-06 13:37:57 -04:00
assert_equal " 1,2,3 " , bind ( " :a " , a : [ 1 , 2 , 3 ] )
assert_equal quoted_abc , bind ( " :a " , a : %w( a b c ) ) # '
2015-11-20 21:34:36 -05:00
2016-08-06 12:26:20 -04:00
assert_equal " 1,2,3 " , bind ( " ? " , SimpleEnumerable . new ( [ 1 , 2 , 3 ] ) )
assert_equal quoted_abc , bind ( " ? " , SimpleEnumerable . new ( %w( a b c ) ) )
2015-11-20 21:34:36 -05:00
2016-08-06 13:37:57 -04:00
assert_equal " 1,2,3 " , bind ( " :a " , a : SimpleEnumerable . new ( [ 1 , 2 , 3 ] ) )
assert_equal quoted_abc , bind ( " :a " , a : SimpleEnumerable . new ( %w( a b c ) ) ) # '
2015-11-20 21:34:36 -05:00
end
def test_bind_empty_enumerable
quoted_nil = ActiveRecord :: Base . connection . quote ( nil )
2016-08-06 12:26:20 -04:00
assert_equal quoted_nil , bind ( " ? " , [ ] )
assert_equal " in ( #{ quoted_nil } ) " , bind ( " in (?) " , [ ] )
assert_equal " foo in ( #{ quoted_nil } ) " , bind ( " foo in (?) " , [ ] )
2015-11-20 21:34:36 -05:00
end
2019-03-01 20:31:59 -05:00
def test_bind_range
quoted_abc = %( #{ ActiveRecord :: Base . connection . quote ( 'a' ) } , #{ ActiveRecord :: Base . connection . quote ( 'b' ) } , #{ ActiveRecord :: Base . connection . quote ( 'c' ) } )
assert_equal " 0 " , bind ( " ? " , 0 .. 0 )
assert_equal " 1,2,3 " , bind ( " ? " , 1 .. 3 )
assert_equal quoted_abc , bind ( " ? " , " a " ... " d " )
end
def test_bind_empty_range
quoted_nil = ActiveRecord :: Base . connection . quote ( nil )
assert_equal quoted_nil , bind ( " ? " , 0 ... 0 )
assert_equal quoted_nil , bind ( " ? " , " a " ... " a " )
end
2015-11-20 21:34:36 -05:00
def test_bind_empty_string
2016-08-06 12:26:20 -04:00
quoted_empty = ActiveRecord :: Base . connection . quote ( " " )
assert_equal quoted_empty , bind ( " ? " , " " )
2015-11-20 21:34:36 -05:00
end
def test_bind_chars
quoted_bambi = ActiveRecord :: Base . connection . quote ( " Bambi " )
quoted_bambi_and_thumper = ActiveRecord :: Base . connection . quote ( " Bambi \n and \n Thumper " )
2016-08-06 12:26:20 -04:00
assert_equal " name= #{ quoted_bambi } " , bind ( " name=? " , " Bambi " )
assert_equal " name= #{ quoted_bambi_and_thumper } " , bind ( " name=? " , " Bambi \n and \n Thumper " )
assert_equal " name= #{ quoted_bambi } " , bind ( " name=? " , " Bambi " . mb_chars )
assert_equal " name= #{ quoted_bambi_and_thumper } " , bind ( " name=? " , " Bambi \n and \n Thumper " . mb_chars )
2015-11-20 21:34:36 -05:00
end
def test_named_bind_with_postgresql_type_casts
2016-08-06 13:37:57 -04:00
l = Proc . new { bind ( " :a::integer '2009-01-01'::date " , a : " 10 " ) }
2015-11-20 21:34:36 -05:00
assert_nothing_raised ( & l )
assert_equal " #{ ActiveRecord :: Base . connection . quote ( '10' ) } ::integer '2009-01-01'::date " , l . call
end
private
def bind ( statement , * vars )
if vars . first . is_a? ( Hash )
ActiveRecord :: Base . send ( :replace_named_bind_variables , statement , vars . first )
else
ActiveRecord :: Base . send ( :replace_bind_variables , statement , vars )
end
end
2008-09-11 16:38:20 -04:00
end