mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
6520ea5f7e
Allowing :controller and :action values to be specified via the path in config/routes.rb has been an underlying cause of a number of issues in Rails that have resulted in security releases. In light of this it's better that controllers and actions are explicitly whitelisted rather than trying to blacklist or sanitize 'bad' values.
203 lines
6.4 KiB
Ruby
203 lines
6.4 KiB
Ruby
require 'abstract_unit'
|
|
|
|
class JsonParamsParsingTest < ActionDispatch::IntegrationTest
|
|
class TestController < ActionController::Base
|
|
class << self
|
|
attr_accessor :last_request_parameters
|
|
end
|
|
|
|
def parse
|
|
self.class.last_request_parameters = request.request_parameters
|
|
head :ok
|
|
end
|
|
end
|
|
|
|
def teardown
|
|
TestController.last_request_parameters = nil
|
|
end
|
|
|
|
test "parses json params for application json" do
|
|
assert_parses(
|
|
{"person" => {"name" => "David"}},
|
|
"{\"person\": {\"name\": \"David\"}}", { 'CONTENT_TYPE' => 'application/json' }
|
|
)
|
|
end
|
|
|
|
test "parses boolean and number json params for application json" do
|
|
assert_parses(
|
|
{"item" => {"enabled" => false, "count" => 10}},
|
|
"{\"item\": {\"enabled\": false, \"count\": 10}}", { 'CONTENT_TYPE' => 'application/json' }
|
|
)
|
|
end
|
|
|
|
test "parses json params for application jsonrequest" do
|
|
assert_parses(
|
|
{"person" => {"name" => "David"}},
|
|
"{\"person\": {\"name\": \"David\"}}", { 'CONTENT_TYPE' => 'application/jsonrequest' }
|
|
)
|
|
end
|
|
|
|
test "does not parse unregistered media types such as application/vnd.api+json" do
|
|
assert_parses(
|
|
{},
|
|
"{\"person\": {\"name\": \"David\"}}", { 'CONTENT_TYPE' => 'application/vnd.api+json' }
|
|
)
|
|
end
|
|
|
|
test "nils are stripped from collections" do
|
|
assert_parses(
|
|
{"person" => []},
|
|
"{\"person\":[null]}", { 'CONTENT_TYPE' => 'application/json' }
|
|
)
|
|
assert_parses(
|
|
{"person" => ['foo']},
|
|
"{\"person\":[\"foo\",null]}", { 'CONTENT_TYPE' => 'application/json' }
|
|
)
|
|
assert_parses(
|
|
{"person" => []},
|
|
"{\"person\":[null, null]}", { 'CONTENT_TYPE' => 'application/json' }
|
|
)
|
|
end
|
|
|
|
test "logs error if parsing unsuccessful" do
|
|
with_test_routing do
|
|
output = StringIO.new
|
|
json = "[\"person]\": {\"name\": \"David\"}}"
|
|
post "/parse", params: json, headers: { 'CONTENT_TYPE' => 'application/json', 'action_dispatch.show_exceptions' => true, 'action_dispatch.logger' => ActiveSupport::Logger.new(output) }
|
|
assert_response :bad_request
|
|
output.rewind && err = output.read
|
|
assert err =~ /Error occurred while parsing request parameters/
|
|
end
|
|
end
|
|
|
|
test "occurring a parse error if parsing unsuccessful" do
|
|
with_test_routing do
|
|
begin
|
|
$stderr = StringIO.new # suppress the log
|
|
json = "[\"person]\": {\"name\": \"David\"}}"
|
|
exception = assert_raise(ActionDispatch::ParamsParser::ParseError) { post "/parse", json, {'CONTENT_TYPE' => 'application/json', 'action_dispatch.show_exceptions' => false} }
|
|
assert_equal JSON::ParserError, exception.cause.class
|
|
assert_equal exception.cause.message, exception.message
|
|
ensure
|
|
$stderr = STDERR
|
|
end
|
|
end
|
|
end
|
|
|
|
test 'raw_post is not empty for JSON request' do
|
|
with_test_routing do
|
|
post '/parse', params: '{"posts": [{"title": "Post Title"}]}', headers: { 'CONTENT_TYPE' => 'application/json' }
|
|
assert_equal '{"posts": [{"title": "Post Title"}]}', request.raw_post
|
|
end
|
|
end
|
|
|
|
private
|
|
def assert_parses(expected, actual, headers = {})
|
|
with_test_routing do
|
|
post "/parse", params: actual, headers: headers
|
|
assert_response :ok
|
|
assert_equal(expected, TestController.last_request_parameters)
|
|
end
|
|
end
|
|
|
|
def with_test_routing
|
|
with_routing do |set|
|
|
set.draw do
|
|
ActiveSupport::Deprecation.silence do
|
|
post ':action', :to => ::JsonParamsParsingTest::TestController
|
|
end
|
|
end
|
|
yield
|
|
end
|
|
end
|
|
end
|
|
|
|
class RootLessJSONParamsParsingTest < ActionDispatch::IntegrationTest
|
|
class UsersController < ActionController::Base
|
|
wrap_parameters :format => :json
|
|
|
|
class << self
|
|
attr_accessor :last_request_parameters, :last_parameters
|
|
end
|
|
|
|
def parse
|
|
self.class.last_request_parameters = request.request_parameters
|
|
self.class.last_parameters = params.to_unsafe_h
|
|
head :ok
|
|
end
|
|
end
|
|
|
|
def teardown
|
|
UsersController.last_request_parameters = nil
|
|
end
|
|
|
|
test "parses json params for application json" do
|
|
assert_parses(
|
|
{"user" => {"username" => "sikachu"}, "username" => "sikachu"},
|
|
"{\"username\": \"sikachu\"}", { 'CONTENT_TYPE' => 'application/json' }
|
|
)
|
|
end
|
|
|
|
test "parses json params for application jsonrequest" do
|
|
assert_parses(
|
|
{"user" => {"username" => "sikachu"}, "username" => "sikachu"},
|
|
"{\"username\": \"sikachu\"}", { 'CONTENT_TYPE' => 'application/jsonrequest' }
|
|
)
|
|
end
|
|
|
|
test "parses json with non-object JSON content" do
|
|
assert_parses(
|
|
{"user" => {"_json" => "string content" }, "_json" => "string content" },
|
|
"\"string content\"", { 'CONTENT_TYPE' => 'application/json' }
|
|
)
|
|
end
|
|
|
|
test "parses json params after custom json mime type registered" do
|
|
begin
|
|
Mime::Type.unregister :json
|
|
Mime::Type.register "application/json", :json, %w(application/vnd.api+json)
|
|
assert_parses(
|
|
{"user" => {"username" => "meinac"}, "username" => "meinac"},
|
|
"{\"username\": \"meinac\"}", { 'CONTENT_TYPE' => 'application/json' }
|
|
)
|
|
ensure
|
|
Mime::Type.unregister :json
|
|
Mime::Type.register "application/json", :json, %w( text/x-json application/jsonrequest )
|
|
end
|
|
end
|
|
|
|
test "parses json params after custom json mime type registered with synonym" do
|
|
begin
|
|
Mime::Type.unregister :json
|
|
Mime::Type.register "application/json", :json, %w(application/vnd.api+json)
|
|
assert_parses(
|
|
{"user" => {"username" => "meinac"}, "username" => "meinac"},
|
|
"{\"username\": \"meinac\"}", { 'CONTENT_TYPE' => 'application/vnd.api+json' }
|
|
)
|
|
ensure
|
|
Mime::Type.unregister :json
|
|
Mime::Type.register "application/json", :json, %w( text/x-json application/jsonrequest )
|
|
end
|
|
end
|
|
|
|
private
|
|
def assert_parses(expected, actual, headers = {})
|
|
with_test_routing(UsersController) do
|
|
post "/parse", params: actual, headers: headers
|
|
assert_response :ok
|
|
assert_equal(expected, UsersController.last_request_parameters)
|
|
assert_equal(expected.merge({"action" => "parse"}), UsersController.last_parameters)
|
|
end
|
|
end
|
|
|
|
def with_test_routing(controller)
|
|
with_routing do |set|
|
|
set.draw do
|
|
ActiveSupport::Deprecation.silence do
|
|
post ':action', :to => controller
|
|
end
|
|
end
|
|
yield
|
|
end
|
|
end
|
|
end
|