Rspec 未捕获错误 StatementInvalid

Rspec is not catching error StatementInvalid

我试图保护自己免受 SQL 注入,我在 #where:

中使用变量作为列名
class BuildSegment::FindUsers

  def initialize(params)
    @key = User.connection.quote_column_name(params[:key])
    @negative = params[:negative]
    @pattern = params[:pattern]
  end

  def call
    if @negative
      users = User.where.not("#{@key} LIKE ?", @pattern)
    else
      users = User.where("#{@key} LIKE ?", @pattern)
    end

    users.uniq
  end
end

变量@key取自可能被用户操纵的Hash。因此,例如,如果 @key = "email LIKE '%' OR email" 并且我将 @key 直接传递给 Active Record 查询,我得到:

SELECT "users".* FROM "users" WHERE (email LIKE '%' OR email LIKE '')

其中 returns 所有用户。我发现 quote_column_name(params[:key]) 作为 returns ActiveRecord::StatementInvalid 错误的解决方案:

ActiveRecord::StatementInvalid:
       PG::UndefinedColumn: ERROR:  column "email LIKE '%com%' OR email" does not exist
       LINE 1: SELECT DISTINCT "users".* FROM "users" WHERE ("email LIKE '%...
                                                             ^
       : SELECT DISTINCT "users".* FROM "users" WHERE ("email LIKE '%com%' OR email" LIKE '')

然而,这个测试失败了:

expect(segment_builder.call).to raise_error(ActiveRecord::StatementError)

以及完整的回溯:

Failures:

  1) BuildSegment is protected from SQL injection
     Failure/Error: users_to_add = users.flatten

     ActiveRecord::StatementInvalid:
       PG::UndefinedColumn: ERROR:  column "email LIKE '%com%' OR email" does not exist
       LINE 1: SELECT DISTINCT "users".* FROM "users" WHERE ("email LIKE '%...
                                                             ^
       : SELECT DISTINCT "users".* FROM "users" WHERE ("email LIKE '%com%' OR email" LIKE '')
     # ./app/services/build_segment.rb:51:in `flatten'
     # ./app/services/build_segment.rb:51:in `users_passing_all_rules'
     # ./app/services/build_segment.rb:46:in `users_passing_filter'
     # ./app/services/build_segment.rb:32:in `block in users_meeting_requirements_for'
     # ./app/services/build_segment.rb:31:in `each'
     # ./app/services/build_segment.rb:31:in `users_meeting_requirements_for'
     # ./app/services/build_segment.rb:8:in `call'
     # ./spec/services/build_segment_spec.rb:107:in `block (2 levels) in <top (required)>'
     # ------------------
     # --- Caused by: ---
     # PG::UndefinedColumn:
     #   ERROR:  column "email LIKE '%com%' OR email" does not exist
     #   LINE 1: SELECT DISTINCT "users".* FROM "users" WHERE ("email LIKE '%...
     #                                                         ^
     #   ./app/services/build_segment.rb:51:in `flatten'

我的错误在哪里?

在我看来你没有正确转义 SQL。

看看这是否适合你:

User.where.not("#{@key} LIKE ?, '#{@pattern}'")

User.where("#{@key} LIKE ?, '#{@pattern}'")

您的代码有两个问题:

1)错误是ActiveRecord::StatementInvalid,不是ActiveRecord::StatementError,而是

2) 您需要使用expect {} 的块语法来捕获异常。有关详细信息,请参阅 https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers/raise-error-matcher