diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 4052115581..abaf146b01 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,3 +1,8 @@ +* Expand payload of `unpermitted_parameters.action_controller` instrumentation to allow subscribers to + know which controller action received unpermitted parameters. + + *bbuchalter* + * Add `ActionController::Live#send_stream` that makes it more convenient to send generated streams: ```ruby diff --git a/actionpack/lib/action_controller/log_subscriber.rb b/actionpack/lib/action_controller/log_subscriber.rb index 2c36d7c759..8323a63712 100644 --- a/actionpack/lib/action_controller/log_subscriber.rb +++ b/actionpack/lib/action_controller/log_subscriber.rb @@ -56,7 +56,9 @@ module ActionController def unpermitted_parameters(event) debug do unpermitted_keys = event.payload[:keys] - color("Unpermitted parameter#{'s' if unpermitted_keys.size > 1}: #{unpermitted_keys.map { |e| ":#{e}" }.join(", ")}", RED) + display_unpermitted_keys = unpermitted_keys.map { |e| ":#{e}" }.join(", ") + context = event.payload[:context].map { |k, v| "#{k}: #{v}" }.join(", ") + color("Unpermitted parameter#{'s' if unpermitted_keys.size > 1}: #{display_unpermitted_keys}. Context: { #{context} }", RED) end end diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb index af129d5e70..b6058f9402 100644 --- a/actionpack/lib/action_controller/metal/strong_parameters.rb +++ b/actionpack/lib/action_controller/metal/strong_parameters.rb @@ -106,11 +106,13 @@ module ActionController # # * +permit_all_parameters+ - If it's +true+, all the parameters will be # permitted by default. The default is +false+. - # * +action_on_unpermitted_parameters+ - Allow to control the behavior when parameters - # that are not explicitly permitted are found. The values can be +false+ to just filter them - # out, :log to additionally write a message on the logger, or :raise to raise - # ActionController::UnpermittedParameters exception. The default value is :log - # in test and development environments, +false+ otherwise. + # * +action_on_unpermitted_parameters+ - Controls behavior when parameters that are not explicitly + # permitted are found. The default value is :log in test and development environments, + # +false+ otherwise. The values can be: + # * +false+ to take no action. + # * :log to emit an ActiveSupport::Notifications.instrument event on the + # unpermitted_parameters.action_controller topic and log at the DEBUG level. + # * :raise to raise a ActionController::UnpermittedParameters exception. # # Examples: # @@ -277,8 +279,9 @@ module ActionController # params = ActionController::Parameters.new(name: "Francesco") # params.permitted? # => true # Person.new(params) # => # - def initialize(parameters = {}) + def initialize(parameters = {}, logging_context = {}) @parameters = parameters.with_indifferent_access + @logging_context = logging_context @permitted = self.class.permit_all_parameters end @@ -968,7 +971,7 @@ module ActionController case self.class.action_on_unpermitted_parameters when :log name = "unpermitted_parameters.action_controller" - ActiveSupport::Notifications.instrument(name, keys: unpermitted_keys) + ActiveSupport::Notifications.instrument(name, keys: unpermitted_keys, context: @logging_context) when :raise raise ActionController::UnpermittedParameters.new(unpermitted_keys) end @@ -1184,7 +1187,15 @@ module ActionController # Returns a new ActionController::Parameters object that # has been instantiated with the request.parameters. def params - @_params ||= Parameters.new(request.parameters) + @_params ||= begin + context = { + controller: self.class.name, + action: action_name, + request: request, + params: request.filtered_parameters + } + Parameters.new(request.parameters, context) + end end # Assigns the given +value+ to the +params+ hash. If +value+ diff --git a/actionpack/test/controller/parameters/log_on_unpermitted_params_test.rb b/actionpack/test/controller/parameters/log_on_unpermitted_params_test.rb index 4fffcf6b10..5c2fe160e6 100644 --- a/actionpack/test/controller/parameters/log_on_unpermitted_params_test.rb +++ b/actionpack/test/controller/parameters/log_on_unpermitted_params_test.rb @@ -13,22 +13,21 @@ class LogOnUnpermittedParamsTest < ActiveSupport::TestCase end test "logs on unexpected param" do - params = ActionController::Parameters.new( - book: { pages: 65 }, - fishing: "Turnips") + request_params = { book: { pages: 65 }, fishing: "Turnips" } + context = { "action" => "my_action", "controller" => "my_controller" } + params = ActionController::Parameters.new(request_params, context) - assert_logged("Unpermitted parameter: :fishing") do + assert_logged("Unpermitted parameter: :fishing. Context: { action: my_action, controller: my_controller }") do params.permit(book: [:pages]) end end test "logs on unexpected params" do - params = ActionController::Parameters.new( - book: { pages: 65 }, - fishing: "Turnips", - car: "Mersedes") + request_params = { book: { pages: 65 }, fishing: "Turnips", car: "Mersedes" } + context = { "action" => "my_action", "controller" => "my_controller" } + params = ActionController::Parameters.new(request_params, context) - assert_logged("Unpermitted parameters: :fishing, :car") do + assert_logged("Unpermitted parameters: :fishing, :car. Context: { action: my_action, controller: my_controller }") do params.permit(book: [:pages]) end end @@ -37,7 +36,7 @@ class LogOnUnpermittedParamsTest < ActiveSupport::TestCase params = ActionController::Parameters.new( book: { pages: 65, title: "Green Cats and where to find then." }) - assert_logged("Unpermitted parameter: :title") do + assert_logged("Unpermitted parameter: :title. Context: { }") do params.permit(book: [:pages]) end end @@ -46,7 +45,7 @@ class LogOnUnpermittedParamsTest < ActiveSupport::TestCase params = ActionController::Parameters.new( book: { pages: 65, title: "Green Cats and where to find then.", author: "G. A. Dog" }) - assert_logged("Unpermitted parameters: :title, :author") do + assert_logged("Unpermitted parameters: :title, :author. Context: { }") do params.permit(book: [:pages]) end end diff --git a/guides/source/active_support_instrumentation.md b/guides/source/active_support_instrumentation.md index e7c5642034..4a2fe6e710 100644 --- a/guides/source/active_support_instrumentation.md +++ b/guides/source/active_support_instrumentation.md @@ -280,9 +280,10 @@ INFO. Additional keys may be added by the caller. ### unpermitted_parameters.action_controller -| Key | Value | -| ------- | ---------------- | -| `:keys` | Unpermitted keys | +| Key | Value | +| ------------- | --------------------------------------------------------------------- | +| `:key` | The unpermitted keys | +| `:context` | Hash with the following keys: :controller, :action, :params, :request | Action Dispatch --------------- diff --git a/guides/source/configuring.md b/guides/source/configuring.md index 2b2bc71c32..d290924aa2 100644 --- a/guides/source/configuring.md +++ b/guides/source/configuring.md @@ -526,7 +526,10 @@ The schema dumper adds two additional configuration options: * `config.action_controller.permit_all_parameters` sets all the parameters for mass assignment to be permitted by default. The default value is `false`. -* `config.action_controller.action_on_unpermitted_parameters` enables logging or raising an exception if parameters that are not explicitly permitted are found. Set to `:log` or `:raise` to enable. The default value is `:log` in development and test environments, and `false` in all other environments. +* `config.action_controller.action_on_unpermitted_parameters` controls behavior when parameters that are not explicitly permitted are found. The default value is `:log` in test and development environments, `false` otherwise. The values can be: + * `false` to take no action + * `:log` to emit an `ActiveSupport::Notifications.instrument` event on the `unpermitted_parameters.action_controller` topic and log at the DEBUG level + * `:raise` to raise a `ActionController::UnpermittedParameters` exception * `config.action_controller.always_permitted_parameters` sets a list of permitted parameters that are permitted by default. The default values are `['controller', 'action']`.