web-dev-qa-db-ja.com

Rails 3.2.11およびRSpecを使用した生JSONデータのPOST

アプリケーションが this exploit に対して脆弱でないことを保証するために、RSpecでそれをカバーするコントローラーテストを作成しようとしています。そうするためには、生のJSONを投稿できる必要がありますが、それを行う方法を見つけていないようです。いくつかの研究を行って、少なくともRAW_POST_DATAヘッダーを使用してそうする方法があると判断しましたが、これはもう機能しないようです:

it "should not be exploitable by using an integer token value" do
  request.env["CONTENT_TYPE"] = "application/json"
  request.env["RAW_POST_DATA"]  = { token: 0 }.to_json
  post :reset_password
end

Paramsハッシュを見ると、トークンはまったく設定されておらず、{ "controller" => "user", "action" => "reset_password" }のみが含まれています。 XMLを使用しようとしても、通常の投稿データを使用しようとしても同じ結果になりますが、すべての場合、期間を設定していないようです。

最近のRails=の脆弱性により、パラメーターがハッシュされる方法が変更されましたが、RSpecを介して生データを投稿する方法はまだありますか?どういうわけかRack::Test::Methods

44

私が知る限り、コントローラー仕様内ではraw POSTデータを送信することはできません。ただし、リクエスト仕様ではかなり簡単に行うことができます。

describe "Example", :type => :request do
  params = { token: 0 }
  post "/user/reset_password", params.to_json, { 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json' }
  #=> params contains { "controller" => "user", "action" => "reset_password", "token" => 0 }
end
75

これは未加工のJSONをコントローラーアクションに送信する方法です(Rails 3以降):

次のようなルートがあるとしましょう:

_post "/users/:username/posts" => "posts#create"_

そして、あなたが体があなたが読むことによって読むjsonであると期待するとしましょう:

JSON.parse(request.body.read)

次に、テストは次のようになります。

_it "should create a post from a json body" do
  json_payload = '{"message": "My opinion is very important"}'
  post :create, json_payload, {format: 'json', username: "larry" }
end
_

_{format: 'json'}_は、それを実現する魔法です。また、TestCase#postのソースを見ると、 http://api.rubyonrails.org/classes/ActionController/TestCase/Behavior.html#method-i-process がかかることがわかります。アクション(json_payload)の後の最初の引数。それが文字列の場合、それを生の投稿本文として設定し、残りの引数を通常どおり解析します。

また、rspecはRailsテストアーキテクチャ上のDSLです。上記のpostメソッドはActionController :: TestCase#postであり、rspecではありません。発明。

11
mastaBlasta

コントローラーのテストで行ったことは、RAW_POST_DATAを明示的に設定することです。

before do
  @request.env['RAW_POST_DATA'] = payload.to_json
  post :my_action
end
10
andrewpurcell

Rails 5の例:

RSpec.describe "Sessions responds to JSON", :type => :request do

  scenario 'with correct authentication' do
    params = {id: 1, format: :json}
    post "/users/sign_in", params: params.to_json, headers: { 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json' }
    expect(response.header['Content-Type']).to include 'application/json'
  end
end
8
Jeremy Lynch

以下に、生のJSONデータを送信するコントローラーテストの完全な動作例を示します。

describe UsersController, :type => :controller do

  describe "#update" do
    context 'when resource is found' do
      before(:each) do
        @user = FactoryGirl.create(:user)
      end

      it 'updates the resource with valid data' do
        @request.headers['Content-Type'] = 'application/vnd.api+json'
        old_email = @user.email
        new_email = Faker::Internet.email
        jsondata = 
        {
          "data" => {
            "type" => "users",
            "id" => @user.id,
            "attributes" => {
              "email" => new_email
            }
          }
        }

        patch :update, jsondata.to_json, jsondata.merge({:id => old_id})

        expect(response.status).to eq(200)
        json_response = JSON.parse(response.body)
        expect(json_response['data']['id']).to eq(@user.id)
        expect(json_response['data']['attributes']['email']).to eq(new_email)
      end
    end
  end
end

重要な部分は次のとおりです。

@request.headers['Content-Type'] = 'application/vnd.api+json'

そして

patch :update, jsondata.to_json, jsondata.merge({:id => old_id})

最初の方法では、リクエストに対してコンテンツタイプが正しく設定されていることを確認します。これは非常に簡単です。 2番目の部分は数時間頭痛の種でしたが、最初のアプローチはかなり異なっていましたが、機能テストで生の投稿データを送信できない Railsバグ があることが判明しました(ただし、統合テストでは許可されます)、これはい回避策ですが、動作します(Rails 4.1.8およびrspec-Rails 3.0.0)。

5
qqbenq