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
?
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
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.
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
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
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).