web-dev-qa-db-fra.com

POSTAGE de données JSON brutes avec Rails 3.2.11 et RSpec

Afin de garantir que mon application n'est pas vulnérable à cet exploit , j'essaie de créer un test de contrôleur dans RSpec pour le couvrir. Pour ce faire, je dois pouvoir publier du JSON brut, mais je n'ai pas semblé trouver de moyen de le faire. En faisant des recherches, j'ai déterminé qu'il existait au moins un moyen de le faire en utilisant le RAW_POST_DATA en-tête, mais cela ne semble plus fonctionner:

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

Quand je regarde le hachage params, le jeton n'est pas défini du tout, et il contient simplement { "controller" => "user", "action" => "reset_password" }. J'obtiens les mêmes résultats lorsque j'essaie d'utiliser XML, ou même lorsque j'essaie simplement d'utiliser des données de publication régulières, dans tous les cas, il semble ne pas définir de période.

Je sais qu'avec les récentes vulnérabilités Rails, la façon dont les paramètres sont hachés a été modifiée, mais y a-t-il encore un moyen de publier des données brutes via RSpec? Puis-je utiliser directement Rack::Test::Methods?

44
Daniel Vandersluis

Pour autant que je sache, l'envoi de données brutes POST n'est plus possible dans une spécification de contrôleur. Cependant, cela peut être fait assez facilement dans une spécification de demande:

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
Daniel Vandersluis

C'est la façon d'envoyer du JSON brut à une action de contrôleur (Rails 3+):

Disons que nous avons un itinéraire comme celui-ci:

post "/users/:username/posts" => "posts#create"

Et disons que vous vous attendez à ce que le corps soit un json que vous lisez en faisant:

JSON.parse(request.body.read)

Ensuite, votre test ressemblera à ceci:

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'} Est la magie qui fait que cela se produit. De plus, si nous regardons la source de TestCase # post http://api.rubyonrails.org/classes/ActionController/TestCase/Behavior.html#method-i-process vous pouvez voir qu'il faut le premier argument après l'action (json_payload) et s'il s'agit d'une chaîne, il le définit comme corps de message brut et analyse le reste des arguments comme normal.

Il est également important de souligner que rspec est simplement un DSL au-dessus de l'architecture de test Rails. La méthode post ci-dessus est le post ActionController :: TestCase # et non pas un rspec invention.

11
mastaBlasta

Ce que nous avons fait dans nos tests de contrôleur est de définir explicitement le RAW_POST_DATA:

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

exemple 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

Voici un exemple de travail complet d'un test de contrôleur envoyant des données json brutes:

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

Les parties importantes sont:

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

et

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

Le premier s'assure que le type de contenu est correctement défini pour votre demande, c'est assez simple. La deuxième partie me donnait des maux de tête pendant quelques heures, mon approche initiale était assez différente, mais il s'est avéré qu'il y a un bug Rails , ce qui nous empêche d'envoyer des données brutes de post dans des tests fonctionnels (mais nous permet dans les tests d'intégration), et c'est une solution de contournement laide, mais cela fonctionne (sur Rails 4.1.8 et rspec-Rails 3.0.0).

5
qqbenq