2013-02-08 104 views
42

爲了確保我的應用程序不易受到this exploit的攻擊,我試圖在RSpec中創建一個控制器測試來覆蓋它。爲了做到這一點,我需要能夠發佈原始的JSON,但我似乎沒有找到辦法做到這一點。在做一些研究,我確定有至少曾經是一個辦法做到這一點使用RAW_POST_DATA頭,但是這似乎並沒有工作了:使用Rails 3.2.11和RSpec發佈原始JSON數據

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

+0

鐵軌4.2.6的,設置'request.env [ 「RAW_POST_DATA」]'在一個RSpec控制器規格爲我工作。 – 2017-06-22 06:04:29

回答

70

就我所知,發送原始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 
+1

這是我發現測試控制器期望原始json POST請求的最乾淨的方式。謝謝。 – sockmonk 2013-04-04 14:00:57

+1

'CONTENT_TYPE'標題已足夠 – 2013-05-18 11:58:17

+15

此解決方案在Rails 3.2.13中不適用於我。我的解決方法是編寫'params = {token:0,format :: json}'。還要在示例中刪除.to_json和它後面的散列。你也可能想在'spec_helper.rb'中有'config.include Rails.application.routes.url_helpers'。使用'response.header ['Content-Type']驗證json響應應該包含'application/json'' – davidtingsu 2013-06-28 20:43:02

10

這是生JSON發送到一個控制器動作(Rails的3+)的方式:

比方說,我們有這樣的路線。然而,可以很容易地在要求規範進行:

post "https://stackoverflow.com/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#後,而不是一些rspec發明。

+0

真棒,我一直在爲這個確切的答案挖了一段時間 – iturgeon 2015-06-02 01:05:04

+0

這似乎不適用於Rails 4 – 2016-08-16 16:53:46

+0

奇怪的事情發生,這對文件工作,並不會在另一個文件上工作。我總是遇到奇怪的問題。 :) – 2016-12-15 10:22:31

8

我們在我們的控制器測試所做的是明確設置RAW_POST_DATA:

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

如果您正在構建需要這種設置的庫,這是以Rails 3,4和5兼容語法獲得此行爲的唯一方法。 – 2017-12-05 15:31:55

5

這裏是一個控制器測試發送原始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}) 

第一個可以確保爲您的請求正確設置內容類型,這非常簡單。第二部分讓我頭痛幾個小時,我的初始方法有點不同,但事實證明有一個Rails bug,它阻止我們在功能測試中發送原始的後期數據(但允許我們進行集成測試),這是一個醜陋的解決方法,但它可以工作(在rails 4.1.8和rspec-rails 3.0.0上)。

6

導軌5例如:

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

    scenario 'with correct authentication' do 
    params = {id: 1, format: :json} 
    post "https://stackoverflow.com/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