Commit dfbe22ffc6ee6aceb585f757c186a4bf3fb010ca

Authored by Andrew Kane
1 parent 19c4165d

Added basic protection from unfiltered parameters to where option [skip ci]

1 ## 5.0.0 (unreleased) 1 ## 5.0.0 (unreleased)
2 2
3 - Searches now use lazy loading (similar to Active Record) 3 - Searches now use lazy loading (similar to Active Record)
  4 +- Added basic protection from unfiltered parameters to `where` option
4 - Anchor regular expressions by default 5 - Anchor regular expressions by default
5 - Raise error when `search` called on relations 6 - Raise error when `search` called on relations
6 - Raise `ArgumentError` (instead of warning) for invalid regular expression modifiers 7 - Raise `ArgumentError` (instead of warning) for invalid regular expression modifiers
lib/searchkick/query.rb
@@ -435,7 +435,7 @@ module Searchkick @@ -435,7 +435,7 @@ module Searchkick
435 payload = {} 435 payload = {}
436 436
437 # type when inheritance 437 # type when inheritance
438 - where = (options[:where] || {}).dup 438 + where = ensure_permitted(options[:where] || {}).dup
439 if searchkick_options[:inheritance] && (options[:type] || (klass != searchkick_klass && searchkick_index)) 439 if searchkick_options[:inheritance] && (options[:type] || (klass != searchkick_klass && searchkick_index))
440 where[:type] = [options[:type] || klass].flatten.map { |v| searchkick_index.klass_document_type(v, true) } 440 where[:type] = [options[:type] || klass].flatten.map { |v| searchkick_index.klass_document_type(v, true) }
441 end 441 end
@@ -832,8 +832,9 @@ module Searchkick @@ -832,8 +832,9 @@ module Searchkick
832 end 832 end
833 833
834 where = {} 834 where = {}
835 - where = (options[:where] || {}).reject { |k| k == field } unless options[:smart_aggs] == false  
836 - agg_filters = where_filters(where.merge(agg_options[:where] || {})) 835 + where = ensure_permitted(options[:where] || {}).reject { |k| k == field } unless options[:smart_aggs] == false
  836 + agg_where = ensure_permitted(agg_options[:where] || {})
  837 + agg_filters = where_filters(where.merge(agg_where))
837 838
838 # only do one level comparison for simplicity 839 # only do one level comparison for simplicity
839 filters.select! do |filter| 840 filters.select! do |filter|
@@ -872,12 +873,13 @@ module Searchkick @@ -872,12 +873,13 @@ module Searchkick
872 payload[:sort] = options[:order].is_a?(Enumerable) ? options[:order] : {options[:order] => :asc} 873 payload[:sort] = options[:order].is_a?(Enumerable) ? options[:order] : {options[:order] => :asc}
873 end 874 end
874 875
875 - def where_filters(where)  
876 - # if where.respond_to?(:permitted?) && !where.permitted?  
877 - # # TODO check in more places  
878 - # Searchkick.warn("Passing unpermitted parameters will raise an exception in Searchkick 5")  
879 - # end 876 + # provides *very* basic protection from unfiltered parameters
  877 + # this is not meant to be comprehensive and may be expanded in the future
  878 + def ensure_permitted(obj)
  879 + obj.to_h
  880 + end
880 881
  882 + def where_filters(where)
881 filters = [] 883 filters = []
882 (where || {}).each do |field, value| 884 (where || {}).each do |field, value|
883 field = :_id if field.to_s == "id" 885 field = :_id if field.to_s == "id"
test/parameters_test.rb
@@ -6,17 +6,30 @@ class ParametersTest < Minitest::Test @@ -6,17 +6,30 @@ class ParametersTest < Minitest::Test
6 super 6 super
7 end 7 end
8 8
9 - def test_where_unpermitted  
10 - # TODO raise error in Searchkick 6  
11 - store [{name: "Product A", store_id: 1}, {name: "Product B", store_id: 2}] 9 + def test_options
12 params = ActionController::Parameters.new({store_id: 1}) 10 params = ActionController::Parameters.new({store_id: 1})
13 - assert_search "product", ["Product A"], where: params 11 + assert_raises(ActionController::UnfilteredParameters) do
  12 + Product.search("*", **params)
  13 + end
  14 + end
  15 +
  16 + def test_where
  17 + params = ActionController::Parameters.new({store_id: 1})
  18 + assert_raises(ActionController::UnfilteredParameters) do
  19 + Product.search("*", where: params)
  20 + end
14 end 21 end
15 22
16 def test_where_permitted 23 def test_where_permitted
17 store [{name: "Product A", store_id: 1}, {name: "Product B", store_id: 2}] 24 store [{name: "Product A", store_id: 1}, {name: "Product B", store_id: 2}]
18 params = ActionController::Parameters.new({store_id: 1}) 25 params = ActionController::Parameters.new({store_id: 1})
19 - assert_search "product", ["Product A"], where: params.permit! 26 + assert_search "product", ["Product A"], where: params.permit(:store_id)
  27 + end
  28 +
  29 + def test_where_value
  30 + store [{name: "Product A", store_id: 1}, {name: "Product B", store_id: 2}]
  31 + params = ActionController::Parameters.new({store_id: 1})
  32 + assert_search "product", ["Product A"], where: {store_id: params[:store_id]}
20 end 33 end
21 34
22 def test_where_hash 35 def test_where_hash
@@ -26,4 +39,18 @@ class ParametersTest < Minitest::Test @@ -26,4 +39,18 @@ class ParametersTest < Minitest::Test
26 end 39 end
27 assert_equal error.message, "can't cast ActionController::Parameters" 40 assert_equal error.message, "can't cast ActionController::Parameters"
28 end 41 end
  42 +
  43 + def test_aggs_where
  44 + params = ActionController::Parameters.new({store_id: 1})
  45 + assert_raises(ActionController::UnfilteredParameters) do
  46 + Product.search("*", aggs: {size: {where: params}})
  47 + end
  48 + end
  49 +
  50 + def test_aggs_where_smart_aggs_false
  51 + params = ActionController::Parameters.new({store_id: 1})
  52 + assert_raises(ActionController::UnfilteredParameters) do
  53 + Product.search("*", aggs: {size: {where: params}}, smart_aggs: false)
  54 + end
  55 + end
29 end 56 end