Skip to content

Commit

Permalink
Support :filter plugin option in error_mail and error_email for filte…
Browse files Browse the repository at this point in the history
…ring parameters, environment variables, and session values (Fixes #346)

For simplicity, this only adds a single option, instead of separate
options for each, and it only supports full filtering of the value,
not partial filtering.  I'm open to expanding this support if users
have a need for it.
  • Loading branch information
jeremyevans committed Jan 4, 2024
1 parent 3d9fe8e commit 1ebf3e0
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 3 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
= master

* Support :filter plugin option in error_mail and error_email for filtering parameters, environment variables, and session values (jeremyevans) (#346)

* Set temporary name on Ruby 3.3 in middleware plugin for middleware class created (janko) (#344)

* Add break plugin, for using break inside a routing block to return from the block and keep routing (jeremyevans)
Expand Down
11 changes: 10 additions & 1 deletion lib/roda/plugins/error_email.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ module RodaPlugins
#
# Options:
#
# :filter :: Callable called with the key and value for each parameter, environment
# variable, and session value. If it returns true, the value of the
# parameter is filtered in the email.
# :from :: The From address to use in the email (required)
# :headers :: A hash of additional headers to use in the email (default: empty hash)
# :host :: The SMTP server to use to send the email (default: localhost)
Expand All @@ -38,6 +41,7 @@ module RodaPlugins
# use an error reporting service instead of this plugin.
module ErrorEmail
DEFAULTS = {
:filter=>lambda{|k,v| false},
:headers=>OPTS,
:host=>'localhost',
# :nocov:
Expand All @@ -52,7 +56,12 @@ module ErrorEmail
{'From'=>h[:from], 'To'=>h[:to], 'Subject'=>"#{h[:prefix]}#{subject}"}
end,
:body=>lambda do |s, e|
format = lambda{|h| h.map{|k, v| "#{k.inspect} => #{v.inspect}"}.sort.join("\n")}
filter = s.opts[:error_email][:filter]
format = lambda do |h|
h = h.map{|k, v| "#{k.inspect} => #{filter.call(k, v) ? 'FILTERED' : v.inspect}"}
h.sort!
h.join("\n")
end

begin
params = s.request.params
Expand Down
15 changes: 13 additions & 2 deletions lib/roda/plugins/error_mail.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ module RodaPlugins
#
# Options:
#
# :filter :: Callable called with the key and value for each parameter, environment
# variable, and session value. If it returns true, the value of the
# parameter is filtered in the email.
# :from :: The From address to use in the email (required)
# :headers :: A hash of additional headers to use in the email (default: empty hash)
# :prefix :: A prefix to use in the email's subject line (default: no prefix)
Expand All @@ -36,9 +39,12 @@ module RodaPlugins
# for low traffic web applications. For high traffic web applications,
# use an error reporting service instead of this plugin.
module ErrorMail
DEFAULT_FILTER = lambda{|k,v| false}
private_constant :DEFAULT_FILTER

# Set default opts for plugin. See ErrorEmail module RDoc for options.
def self.configure(app, opts=OPTS)
app.opts[:error_mail] = email_opts = (app.opts[:error_mail] || OPTS).merge(opts).freeze
app.opts[:error_mail] = email_opts = (app.opts[:error_mail] || {:filter=>DEFAULT_FILTER}).merge(opts).freeze
unless email_opts[:to] && email_opts[:from]
raise RodaError, "must provide :to and :from options to error_mail plugin"
end
Expand Down Expand Up @@ -68,8 +74,13 @@ def _error_mail(e)
e.to_s
end
subject = "#{email_opts[:prefix]}#{subject}"
filter = email_opts[:filter]

format = lambda{|h| h.map{|k, v| "#{k.inspect} => #{v.inspect}"}.sort.join("\n")}
format = lambda do |h|
h = h.map{|k, v| "#{k.inspect} => #{filter.call(k, v) ? 'FILTERED' : v.inspect}"}
h.sort!
h.join("\n")
end

begin
params = request.params
Expand Down
10 changes: 10 additions & 0 deletions spec/plugin/error_email_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,16 @@ def email
b.must_match(/^Backtrace:$.+^ENV:$.+^"rack\.input" => .+^Params:$\s+^"b" => "c"$\s+^Session:$\s+^"d" => "e"$/m)
end

it "supports :filter plugin option for filtering parameters, environment variables, and session values" do
app.route do |r|
raise ArgumentError, 'bad foo' rescue error_email_content($!)
end
app.plugin :error_email, :filter=>proc{|k, v| k == 'b' || k == 'd' || k == 'rack.input'}
b = body('rack.input'=>rack_input, 'QUERY_STRING'=>'b=c&f=g', 'rack.session'=>{'d'=>'e', 'h'=>'i'})
b.must_match(/^Subject: ArgumentError: bad foo/)
b.must_match(/^Backtrace:.+^ENV:.+^"rack\.input" => FILTERED.+^Params:\s+^"b" => FILTERED\s+"f" => "g"\s+^Session:\s+^"d" => FILTERED\s+"h" => "i"/m)
end

it "handles invalid parameters in error_email_content" do
app.route do |r|
raise ArgumentError, 'bad foo' rescue error_email_content($!)
Expand Down
10 changes: 10 additions & 0 deletions spec/plugin/error_mail_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,16 @@ def email
b.must_match(/^Backtrace:.+^ENV:.+^"rack\.input" => .+^Params:\s+^"b" => "c"\s+^Session:\s+^"d" => "e"/m)
end

it "supports :filter plugin option for filtering parameters, environment variables, and session values" do
app.route do |r|
raise ArgumentError, 'bad foo' rescue error_mail_content($!)
end
app.plugin :error_mail, :filter=>proc{|k, v| k == 'b' || k == 'd' || k == 'rack.input'}
b = body('rack.input'=>rack_input, 'QUERY_STRING'=>'b=c&f=g', 'rack.session'=>{'d'=>'e', 'h'=>'i'})
b.must_match(/^Subject: ArgumentError: bad foo/)
b.must_match(/^Backtrace:.+^ENV:.+^"rack\.input" => FILTERED.+^Params:\s+^"b" => FILTERED\s+"f" => "g"\s+^Session:\s+^"d" => FILTERED\s+"h" => "i"/m)
end

it "handles invalid parameters in error_mail_content" do
app.route do |r|
raise ArgumentError, 'bad foo' rescue error_mail_content($!)
Expand Down

0 comments on commit 1ebf3e0

Please sign in to comment.