Back to Repositories

Testing Parameter Security and Filtering in Searchkick

This test suite validates parameter handling and security in Searchkick, focusing on ActionController::Parameters integration and proper parameter filtering for search operations.

Test Coverage Overview

The test suite comprehensively covers parameter handling in search operations, including:

  • Unfiltered parameter validation
  • Permitted parameter handling
  • Parameter value processing
  • Hash parameter casting
  • Aggregation parameter security

Implementation Analysis

Tests utilize Minitest framework with a systematic approach to parameter validation. The implementation focuses on both direct search methods and relation-based queries, employing various parameter permutation scenarios.

Key patterns include error handling for unfiltered parameters, permitted parameter verification, and proper type casting validation.

Technical Details

Testing components include:

  • Minitest as the testing framework
  • ActionController::Parameters for parameter handling
  • Custom assertions for search validation
  • Store helper methods for test data setup

Best Practices Demonstrated

The test suite exemplifies robust security testing practices by:

  • Thorough validation of parameter filtering
  • Comprehensive error case coverage
  • Consistent assertion patterns
  • Clear test organization by parameter type and usage context

ankane/searchkick

test/parameters_test.rb

            
require_relative "test_helper"

class ParametersTest < Minitest::Test
  def setup
    require "action_controller"
    super
  end

  def test_options
    params = ActionController::Parameters.new({store_id: 1})
    assert_raises(ActionController::UnfilteredParameters) do
      Product.search("*", **params)
    end
  end

  def test_where
    params = ActionController::Parameters.new({store_id: 1})
    assert_raises(ActionController::UnfilteredParameters) do
      Product.search("*", where: params)
    end
  end

  def test_where_relation
    params = ActionController::Parameters.new({store_id: 1})
    assert_raises(ActionController::UnfilteredParameters) do
      Product.search("*").where(params)
    end
  end

  def test_rewhere_relation
    params = ActionController::Parameters.new({store_id: 1})
    assert_raises(ActionController::UnfilteredParameters) do
      Product.search("*").where(params)
    end
  end

  def test_where_permitted
    store [{name: "Product A", store_id: 1}, {name: "Product B", store_id: 2}]
    params = ActionController::Parameters.new({store_id: 1})
    assert_search "product", ["Product A"], where: params.permit(:store_id)
  end

  def test_where_permitted_relation
    store [{name: "Product A", store_id: 1}, {name: "Product B", store_id: 2}]
    params = ActionController::Parameters.new({store_id: 1})
    assert_search_relation ["Product A"], Product.search("product").where(params.permit(:store_id))
  end

  def test_rewhere_permitted_relation
    store [{name: "Product A", store_id: 1}, {name: "Product B", store_id: 2}]
    params = ActionController::Parameters.new({store_id: 1})
    assert_search_relation ["Product A"], Product.search("product").rewhere(params.permit(:store_id))
  end

  def test_where_value
    store [{name: "Product A", store_id: 1}, {name: "Product B", store_id: 2}]
    params = ActionController::Parameters.new({store_id: 1})
    assert_search "product", ["Product A"], where: {store_id: params[:store_id]}
  end

  def test_where_value_relation
    store [{name: "Product A", store_id: 1}, {name: "Product B", store_id: 2}]
    params = ActionController::Parameters.new({store_id: 1})
    assert_search_relation ["Product A"], Product.search("product").where(store_id: params[:store_id])
  end

  def test_rewhere_value_relation
    store [{name: "Product A", store_id: 1}, {name: "Product B", store_id: 2}]
    params = ActionController::Parameters.new({store_id: 1})
    assert_search_relation ["Product A"], Product.search("product").where(store_id: params[:store_id])
  end

  def test_where_hash
    params = ActionController::Parameters.new({store_id: {value: 10, boost: 2}})
    error = assert_raises(TypeError) do
      assert_search "product", [], where: {store_id: params[:store_id]}
    end
    assert_equal error.message, "can't cast ActionController::Parameters"
  end

  # TODO raise error without to_a
  def test_where_hash_relation
    params = ActionController::Parameters.new({store_id: {value: 10, boost: 2}})
    error = assert_raises(TypeError) do
      Product.search("product").where(store_id: params[:store_id]).to_a
    end
    assert_equal error.message, "can't cast ActionController::Parameters"
  end

  # TODO raise error without to_a
  def test_rewhere_hash_relation
    params = ActionController::Parameters.new({store_id: {value: 10, boost: 2}})
    error = assert_raises(TypeError) do
      Product.search("product").rewhere(store_id: params[:store_id]).to_a
    end
    assert_equal error.message, "can't cast ActionController::Parameters"
  end

  def test_aggs_where
    params = ActionController::Parameters.new({store_id: 1})
    assert_raises(ActionController::UnfilteredParameters) do
      Product.search("*", aggs: {size: {where: params}})
    end
  end

  def test_aggs_where_smart_aggs_false
    params = ActionController::Parameters.new({store_id: 1})
    assert_raises(ActionController::UnfilteredParameters) do
      Product.search("*", aggs: {size: {where: params}}, smart_aggs: false)
    end
  end
end