2013-02-08 11 views
42

Per garantire che la mia applicazione non sia vulnerabile a this exploit, sto cercando di creare un test del controller in RSpec per coprirlo. Per fare ciò, devo essere in grado di pubblicare JSON raw, ma non ho trovato il modo di farlo. Nel fare alcune ricerche, ho deciso che almeno ha usato per essere un modo per farlo utilizzando l'intestazione RAW_POST_DATA, ma questo non sembra funzionare più:POSTING di dati JSON non elaborati con Rails 3.2.11 e RSpec

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 

Quando guardo l'hash params , il token non è impostato, e contiene solo { "controller" => "user", "action" => "reset_password" }. Ottengo gli stessi risultati quando provo a usare XML, o anche quando provo a usare solo i dati dei post regolari, in tutti i casi, sembra non impostarlo.

So che con le recenti vulnerabilità di Rails, il modo in cui i parametri sono sottoposti a hash è stato modificato, ma esiste ancora un modo per pubblicare dati grezzi tramite RSpec? Posso in qualche modo utilizzare direttamente lo Rack::Test::Methods?

+0

Come di Rails 4.2.6, l'impostazione 'request.env [ "RAW_POST_DATA"]' in un controller spec RSpec sta lavorando per me. –

risposta

70

Per quanto ne so, l'invio di dati POST non è più possibile all'interno di una specifica del controller. Tuttavia, può essere fatto abbastanza facilmente in una richiesta specifica:

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

Questo è il modo più pulito che ho trovato per testare i controller che si aspettano richieste POST JSON non elaborate. Grazie. – sockmonk

+1

L'intestazione 'CONTENT_TYPE' è abbastanza –

+15

questa soluzione non funziona per me in Rails 3.2.13. La mia soluzione era scrivere 'params = {token: 0, format:: json}'. Rimuovi anche .to_json e l'hash che lo segue nell'esempio. Inoltre potresti voler avere 'config.include Rails.application.routes.url_helpers' in' spec_helper.rb'. Verificare la risposta JSON con 'response.header ['Content-Type']. Dovrebbe includere 'application/json'' – davidtingsu

10

Questo è il modo di inviare JSON grezzo ad un'azione di controllo (Rails 3+):

Diciamo che abbiamo un percorso come questo :

post "https://stackoverflow.com/users/:username/posts" => "posts#create"

E diciamo che ci si aspetta il corpo per essere un jSON leggere facendo:

JSON.parse(request.body.read)

Poi il test sarà simile a questa:

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'} è la magia che fa accadere. Inoltre, se guardiamo all'origine per # # di TestCase post http://api.rubyonrails.org/classes/ActionController/TestCase/Behavior.html#method-i-process puoi vedere che prende il primo argomento dopo l'azione (json_payload) e se è una stringa lo imposta come corpo del post grezzo e analizza il resto degli argomenti come normale.

È anche importante sottolineare che rspec è semplicemente un DSL in cima all'architettura di test di Rails. Il metodo post sopra è il post di ActionController :: TestCase # e non qualche invenzione di rspec.

+0

Impressionante, sto scavando da parecchio tempo per questa risposta esatta – iturgeon

+0

Questo non sembra funzionare per Rails 4 –

+0

cose strane accadono, questo funziona su un file e non funzionerà su un altro file. Incontro sempre problemi strani. :) –

8

Quello che abbiamo fatto nei nostri test regolatore è impostato in modo esplicito il RAW_POST_DATA:

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

Se stai costruendo una libreria che ha bisogno di questo tipo di installazione, questo è l'unico modo per ottenere questo comportamento con una sintassi conforme a Rails 3, 4 e 5. –

5

Ecco un esempio di lavoro pieno di un test di controllo invio di dati JSON grezzi:

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 

L'importante le parti sono:

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

e

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

Il primo si assicura che il tipo di contenuto sia impostato correttamente per la richiesta, questo è piuttosto semplice. La seconda parte mi ha dato mal di testa per alcune ore, il mio approccio iniziale era abbastanza diverso, ma si è scoperto che c'è un Rails bug, che ci impedisce di inviare dati di post grezzi in test funzionali (ma ci permette di test di integrazione), e questa è una brutta soluzione, ma funziona (su binari 4.1.8 e rspec-rails 3.0.0).

6

Rails 5 Esempio:

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 
Problemi correlati