diff options
Diffstat (limited to 'actionpack')
12 files changed, 95 insertions, 19 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 9fcff6a6ca..d9046f1431 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,3 +1,5 @@ +* Fix strong parameters blocks all attributes even when only some keys are invalid (non-numerical). It should only block invalid key's values instead. + *Stan Lo* Please check [6-0-stable](https://github.com/rails/rails/blob/6-0-stable/actionpack/CHANGELOG.md) for previous changes. diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb index ae774b01f1..8bde82ccca 100644 --- a/actionpack/lib/action_controller/metal/strong_parameters.rb +++ b/actionpack/lib/action_controller/metal/strong_parameters.rb @@ -223,6 +223,12 @@ module ActionController # config.always_permitted_parameters = %w( controller action format ) cattr_accessor :always_permitted_parameters, default: %w( controller action ) + class << self + def nested_attribute?(key, value) # :nodoc: + key =~ /\A-?\d+\z/ && (value.is_a?(Hash) || value.is_a?(Parameters)) + end + end + # Returns a new instance of <tt>ActionController::Parameters</tt>. # Also, sets the +permitted+ attribute to the default value of # <tt>ActionController::Parameters.permit_all_parameters</tt>. @@ -811,8 +817,14 @@ module ActionController attr_writer :permitted - def fields_for_style? - @parameters.all? { |k, v| k =~ /\A-?\d+\z/ && (v.is_a?(Hash) || v.is_a?(Parameters)) } + def nested_attributes? + @parameters.any? { |k, v| Parameters.nested_attribute?(k, v) } + end + + def each_nested_attribute + hash = self.class.new + self.each { |k, v| hash[k] = yield v if Parameters.nested_attribute?(k, v) } + hash end private @@ -857,15 +869,13 @@ module ActionController end end - def each_element(object) + def each_element(object, &block) case object when Array object.grep(Parameters).map { |el| yield el }.compact when Parameters - if object.fields_for_style? - hash = object.class.new - object.each { |k, v| hash[k] = yield v } - hash + if object.nested_attributes? + object.each_nested_attribute(&block) else yield object end diff --git a/actionpack/lib/action_dispatch/middleware/debug_view.rb b/actionpack/lib/action_dispatch/middleware/debug_view.rb index a03650254e..148662a48b 100644 --- a/actionpack/lib/action_dispatch/middleware/debug_view.rb +++ b/actionpack/lib/action_dispatch/middleware/debug_view.rb @@ -56,5 +56,11 @@ module ActionDispatch def protect_against_forgery? false end + + def params_valid? + @request.parameters + rescue ActionController::BadRequest + false + end end end diff --git a/actionpack/lib/action_dispatch/middleware/stack.rb b/actionpack/lib/action_dispatch/middleware/stack.rb index f0c869fba0..57e4adb457 100644 --- a/actionpack/lib/action_dispatch/middleware/stack.rb +++ b/actionpack/lib/action_dispatch/middleware/stack.rb @@ -34,7 +34,11 @@ module ActionDispatch end def build(app) - InstrumentationProxy.new(klass.new(app, *args, &block), inspect) + klass.new(app, *args, &block) + end + + def build_instrumented(app) + InstrumentationProxy.new(build(app), inspect) end end @@ -119,7 +123,14 @@ module ActionDispatch end def build(app = nil, &block) - middlewares.freeze.reverse.inject(app || block) { |a, e| e.build(a) } + instrumenting = ActiveSupport::Notifications.notifier.listening?(InstrumentationProxy::EVENT_NAME) + middlewares.freeze.reverse.inject(app || block) do |a, e| + if instrumenting + e.build_instrumented(a) + else + e.build(a) + end + end end private diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb index 49b1e83551..04271d8e8a 100644 --- a/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb @@ -6,7 +6,9 @@ <% end %> <h2 style="margin-top: 30px">Request</h2> -<p><b>Parameters</b>:</p> <pre><%= debug_params(@request.filtered_parameters) %></pre> +<% if params_valid? %> + <p><b>Parameters</b>:</p> <pre><%= debug_params(@request.filtered_parameters) %></pre> +<% end %> <div class="details"> <div class="summary"><a href="#" onclick="return toggleSessionDump()">Toggle session dump</a></div> diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb index 396768ecee..ca42a6fa8b 100644 --- a/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb @@ -1,5 +1,5 @@ <% - clean_params = @request.filtered_parameters.clone + clean_params = params_valid? ? @request.filtered_parameters.clone : {} clean_params.delete("action") clean_params.delete("controller") diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb index 999e84e4d6..57cdcf9aaf 100644 --- a/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb @@ -1,7 +1,7 @@ <header> <h1> <%= @exception.class.to_s %> - <% if @request.parameters['controller'] %> + <% if params_valid? && @request.parameters['controller'] %> in <%= @request.parameters['controller'].camelize %>Controller<% if @request.parameters['action'] %>#<%= @request.parameters['action'] %><% end %> <% end %> </h1> diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb index 603de54b8b..d3265563a8 100644 --- a/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb @@ -1,5 +1,5 @@ <%= @exception.class.to_s %><% - if @request.parameters['controller'] + if params_valid? && @request.parameters['controller'] %> in <%= @request.parameters['controller'].camelize %>Controller<% if @request.parameters['action'] %>#<%= @request.parameters['action'] %><% end %> <% end %> diff --git a/actionpack/test/controller/parameters/nested_parameters_permit_test.rb b/actionpack/test/controller/parameters/nested_parameters_permit_test.rb index 1403e224c0..6243b5c51b 100644 --- a/actionpack/test/controller/parameters/nested_parameters_permit_test.rb +++ b/actionpack/test/controller/parameters/nested_parameters_permit_test.rb @@ -125,7 +125,7 @@ class NestedParametersPermitTest < ActiveSupport::TestCase assert_nil permitted[:book][:genre] end - test "fields_for-style nested params" do + test "nested params with numeric keys" do params = ActionController::Parameters.new( book: { authors_attributes: { @@ -150,7 +150,33 @@ class NestedParametersPermitTest < ActiveSupport::TestCase assert_filtered_out permitted[:book][:authors_attributes]["0"], :age_of_death end - test "fields_for-style nested params with negative numbers" do + test "nested params with non_numeric keys" do + params = ActionController::Parameters.new( + book: { + authors_attributes: { + '0': { name: "William Shakespeare", age_of_death: "52" }, + '1': { name: "Unattributed Assistant" }, + '2': "Not a hash", + 'new_record': { name: "Some name" } + } + }) + permitted = params.permit book: { authors_attributes: [ :name ] } + + assert_not_nil permitted[:book][:authors_attributes]["0"] + assert_not_nil permitted[:book][:authors_attributes]["1"] + + assert_nil permitted[:book][:authors_attributes]["2"] + assert_nil permitted[:book][:authors_attributes]["new_record"] + assert_equal "William Shakespeare", permitted[:book][:authors_attributes]["0"][:name] + assert_equal "Unattributed Assistant", permitted[:book][:authors_attributes]["1"][:name] + + assert_equal( + { "book" => { "authors_attributes" => { "0" => { "name" => "William Shakespeare" }, "1" => { "name" => "Unattributed Assistant" } } } }, + permitted.to_h + ) + end + + test "nested params with negative numeric keys" do params = ActionController::Parameters.new( book: { authors_attributes: { diff --git a/actionpack/test/controller/show_exceptions_test.rb b/actionpack/test/controller/show_exceptions_test.rb index 8724f9bcdb..1d68a359dc 100644 --- a/actionpack/test/controller/show_exceptions_test.rb +++ b/actionpack/test/controller/show_exceptions_test.rb @@ -99,7 +99,7 @@ module ShowExceptions class ShowFailsafeExceptionsTest < ActionDispatch::IntegrationTest def test_render_failsafe_exception @app = ShowExceptionsOverriddenController.action(:boom) - middleware = @app.instance_variable_get(:@middleware) + middleware = @app @exceptions_app = middleware.instance_variable_get(:@exceptions_app) middleware.instance_variable_set(:@exceptions_app, nil) $stderr = StringIO.new diff --git a/actionpack/test/dispatch/debug_exceptions_test.rb b/actionpack/test/dispatch/debug_exceptions_test.rb index 5ae8a20ae4..3e57e8f4d9 100644 --- a/actionpack/test/dispatch/debug_exceptions_test.rb +++ b/actionpack/test/dispatch/debug_exceptions_test.rb @@ -620,4 +620,23 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest assert_select 'input[value="Action 2"]' end end + + test "debug exceptions app shows diagnostics when malformed query parameters are provided" do + @app = DevelopmentApp + + get "/bad_request?x[y]=1&x[y][][w]=2" + + assert_response 400 + assert_match "ActionController::BadRequest", body + end + + test "debug exceptions app shows diagnostics when malformed query parameters are provided by XHR" do + @app = DevelopmentApp + xhr_request_env = { "action_dispatch.show_exceptions" => true, "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest" } + + get "/bad_request?x[y]=1&x[y][][w]=2", headers: xhr_request_env + + assert_response 400 + assert_match "ActionController::BadRequest", body + end end diff --git a/actionpack/test/dispatch/middleware_stack_test.rb b/actionpack/test/dispatch/middleware_stack_test.rb index 90f2eccd19..c534e60c74 100644 --- a/actionpack/test/dispatch/middleware_stack_test.rb +++ b/actionpack/test/dispatch/middleware_stack_test.rb @@ -121,9 +121,6 @@ class MiddlewareStackTest < ActiveSupport::TestCase end test "instruments the execution of middlewares" do - app = @stack.build(proc { |env| [200, {}, []] }) - env = {} - events = [] subscriber = proc do |*args| @@ -131,6 +128,9 @@ class MiddlewareStackTest < ActiveSupport::TestCase end ActiveSupport::Notifications.subscribed(subscriber, "process_middleware.action_dispatch") do + app = @stack.build(proc { |env| [200, {}, []] }) + + env = {} app.call(env) end |