Added ActionController.filter_parameter_logging that makes it easy to remove passwords, credit card numbers, and other sensitive information from being logged when a request is handled #1897 [jeremye@bsa.ca.gov]

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@4200 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
David Heinemeier Hansson 2006-04-07 22:26:25 +00:00
parent bbbe4247a5
commit 47b74e6e16
3 changed files with 95 additions and 1 deletions

@ -1,3 +1,8 @@
*SVN*
* Added ActionController.filter_parameter_logging that makes it easy to remove passwords, credit card numbers, and other sensitive information from being logged when a request is handled #1897 [jeremye@bsa.ca.gov]
*1.12.1* (April 6th, 2005)
* Fixed that template extensions would be cached development mode #4624 [Stefan Kaes]

@ -366,6 +366,53 @@ def hidden_actions
def hide_action(*names)
write_inheritable_attribute(:hidden_actions, hidden_actions | names.collect { |n| n.to_s })
end
# Replace sensitive paramater data from the request log.
# Filters paramaters that have any of the arguments as a substring.
# Looks in all subhashes of the param hash for keys to filter.
# If a block is given, each key and value of the paramater hash and all
# subhashes is passed to it, the value or key
# can be replaced using String#replace or similar method.
#
# Examples:
# filter_parameter_logging
# => Does nothing, just slows the logging process down
#
# filter_parameter_logging :password
# => replaces the value to all keys matching /password/i with "[FILTERED]"
#
# filter_parameter_logging :foo, "bar"
# => replaces the value to all keys matching /foo|bar/i with "[FILTERED]"
#
# filter_parameter_logging { |k,v| v.reverse! if k =~ /secret/i }
# => reverses the value to all keys matching /secret/i
#
# filter_parameter_logging(:foo, "bar") { |k,v| v.reverse! if k =~ /secret/i }
# => reverses the value to all keys matching /secret/i, and
# replaces the value to all keys matching /foo|bar/i with "[FILTERED]"
def filter_parameter_logging(*filter_words, &block)
parameter_filter = Regexp.new(filter_words.collect{ |s| s.to_s }.join('|'), true) if filter_words.length > 0
define_method(:filter_parameters) do |unfiltered_parameters|
filtered_parameters = {}
unfiltered_parameters.each do |key, value|
if key =~ parameter_filter
filtered_parameters[key] = '[FILTERED]'
elsif value.is_a?(Hash)
filtered_parameters[key] = filter_parameters(value)
elsif block_given?
key, value = key.dup, value.dup
yield key, value
filtered_parameters[key] = value
else
filtered_parameters[key] = value
end
end
filtered_parameters
end
end
end
public
@ -901,7 +948,7 @@ def log_processing
if logger
logger.info "\n\nProcessing #{controller_class_name}\##{action_name} (for #{request_origin}) [#{request.method.to_s.upcase}]"
logger.info " Session ID: #{@session.session_id}" if @session and @session.respond_to?(:session_id)
logger.info " Parameters: #{@params.inspect}"
logger.info " Parameters: #{respond_to?(:filter_parameters) ? filter_parameters(@params).inspect : @params.inspect}"
end
end

@ -0,0 +1,42 @@
require File.dirname(__FILE__) + '/../abstract_unit'
class FilterParamController < ActionController::Base
end
class FilterParamTest < Test::Unit::TestCase
def setup
@controller = FilterParamController.new
end
def test_filter_parameters
assert FilterParamController.respond_to?(:filter_parameter_logging)
assert !@controller.respond_to?(:filter_parameters)
FilterParamController.filter_parameter_logging
assert @controller.respond_to?(:filter_parameters)
test_hashes = [[{},{},[]],
[{'foo'=>'bar'},{'foo'=>'bar'},[]],
[{'foo'=>'bar'},{'foo'=>'bar'},%w'food'],
[{'foo'=>'bar'},{'foo'=>'[FILTERED]'},%w'foo'],
[{'foo'=>'bar', 'bar'=>'foo'},{'foo'=>'[FILTERED]', 'bar'=>'foo'},%w'foo baz'],
[{'foo'=>'bar', 'baz'=>'foo'},{'foo'=>'[FILTERED]', 'baz'=>'[FILTERED]'},%w'foo baz'],
[{'bar'=>{'foo'=>'bar','bar'=>'foo'}},{'bar'=>{'foo'=>'[FILTERED]','bar'=>'foo'}},%w'fo'],
[{'foo'=>{'foo'=>'bar','bar'=>'foo'}},{'foo'=>'[FILTERED]'},%w'f banana']]
test_hashes.each do |before_filter, after_filter, filter_words|
FilterParamController.filter_parameter_logging(*filter_words)
assert_equal after_filter, @controller.filter_parameters(before_filter)
filter_words.push('blah')
FilterParamController.filter_parameter_logging(*filter_words) do |key, value|
value.reverse! if key =~ /bargain/
end
before_filter['barg'] = {'bargain'=>'gain', 'blah'=>'bar', 'bar'=>{'bargain'=>{'blah'=>'foo'}}}
after_filter['barg'] = {'bargain'=>'niag', 'blah'=>'[FILTERED]', 'bar'=>{'bargain'=>{'blah'=>'[FILTERED]'}}}
assert_equal after_filter, @controller.filter_parameters(before_filter)
end
end
end