ruby on rails - PUBLICACIÓN de datos JSON sin procesar con Rails 3.2.11 y RSpec
ruby-on-rails ruby-on-rails-3.2 (5)
Para garantizar que mi aplicación no sea vulnerable a este exploit , intento crear una prueba de controlador en RSpec para cubrirla. Para poder hacerlo, necesito poder publicar JSON sin procesar, pero no he encontrado una forma de hacerlo. Al hacer algunas investigaciones, he determinado que al menos solía haber una manera de hacerlo usando el encabezado RAW_POST_DATA
, pero parece que ya no funciona:
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
Cuando miro el hash de parámetros, token no está configurado en absoluto, y solo contiene { "controller" => "user", "action" => "reset_password" }
. Obtengo los mismos resultados cuando trato de usar XML, o incluso cuando intento simplemente usar datos de publicación regulares, en todos los casos, parece que no establece un período.
Sé que con las recientes vulnerabilidades de Rails, la forma en que se procesan los parámetros ha sido cambiada, ¿pero todavía hay una forma de publicar datos sin procesar a través de RSpec? ¿De alguna manera puedo usar Rack::Test::Methods
manera directa?
Aquí hay un ejemplo completo de trabajo de una prueba de controlador que envía datos raw 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
Las partes importantes son:
@request.headers[''Content-Type''] = ''application/vnd.api+json''
y
patch :update, jsondata.to_json, jsondata.merge({:id => old_id})
El primero se asegura de que el tipo de contenido esté configurado correctamente para su solicitud, esto es bastante sencillo. La segunda parte me estaba dando dolores de cabeza durante unas horas, mi enfoque inicial fue bastante diferente, pero resultó que hay un error de Rails , que nos impide enviar datos brutos de publicaciones en pruebas funcionales (pero nos permite las pruebas de integración) ), y esta es una solución fea, pero funciona (en rieles 4.1.8 y rspec-rails 3.0.0).
Esta es la forma de enviar JSON sin formato a una acción de controlador (Rails 3+):
Digamos que tenemos una ruta como esta:
post "/users/:username/posts" => "posts#create"
Y digamos que esperas que el cuerpo sea un json que lees al hacer:
JSON.parse(request.body.read)
Entonces su prueba se verá así:
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''}
es la magia que lo hace posible. Además, si miramos la fuente de TestCase # post http://api.rubyonrails.org/classes/ActionController/TestCase/Behavior.html#method-i-process , puede ver que toma el primer argumento después de la acción ( json_payload) y si es una cadena, lo establece como cuerpo de publicación sin procesar, y analiza el resto de los argumentos de forma normal.
También es importante señalar que rspec es simplemente una DSL sobre la arquitectura de prueba de Rails. El método de post
anterior es la publicación ActionController :: TestCase # y no una invención de rspec.
Lo que hemos hecho en nuestras pruebas de controlador se establece explícitamente como RAW_POST_DATA:
before do
@request.env[''RAW_POST_DATA''] = payload.to_json
post :my_action
end
Por lo que he podido decir, el envío de datos POST brutos ya no es posible dentro de una especificación de controlador. Sin embargo, se puede hacer con bastante facilidad en una especificación de solicitud:
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
Ejemplo de 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