honeybadger 2.7.2 → 3.0.0.beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +58 -3
- data/README.md +8 -15
- data/lib/honeybadger.rb +9 -232
- data/lib/honeybadger/agent.rb +292 -134
- data/lib/honeybadger/backend.rb +6 -6
- data/lib/honeybadger/backend/base.rb +11 -0
- data/lib/honeybadger/backend/server.rb +2 -14
- data/lib/honeybadger/cli.rb +0 -2
- data/lib/honeybadger/cli/deploy.rb +42 -0
- data/lib/honeybadger/cli/exec.rb +138 -0
- data/lib/honeybadger/cli/heroku.rb +1 -22
- data/lib/honeybadger/cli/install.rb +74 -0
- data/lib/honeybadger/cli/main.rb +138 -153
- data/lib/honeybadger/cli/notify.rb +66 -0
- data/lib/honeybadger/cli/test.rb +266 -0
- data/lib/honeybadger/config.rb +178 -162
- data/lib/honeybadger/config/defaults.rb +5 -5
- data/lib/honeybadger/config/env.rb +8 -6
- data/lib/honeybadger/config/ruby.rb +100 -0
- data/lib/honeybadger/config/yaml.rb +18 -19
- data/lib/honeybadger/const.rb +3 -16
- data/lib/honeybadger/context_manager.rb +50 -0
- data/lib/honeybadger/init/rails.rb +9 -21
- data/lib/honeybadger/init/rake.rb +2 -0
- data/lib/honeybadger/init/ruby.rb +9 -0
- data/lib/honeybadger/init/sinatra.rb +13 -6
- data/lib/honeybadger/notice.rb +29 -14
- data/lib/honeybadger/plugins/delayed_job/plugin.rb +4 -5
- data/lib/honeybadger/plugins/passenger.rb +1 -2
- data/lib/honeybadger/plugins/rails.rb +0 -28
- data/lib/honeybadger/plugins/resque.rb +2 -5
- data/lib/honeybadger/plugins/shoryuken.rb +2 -2
- data/lib/honeybadger/plugins/sidekiq.rb +2 -2
- data/lib/honeybadger/plugins/sucker_punch.rb +1 -0
- data/lib/honeybadger/plugins/thor.rb +2 -2
- data/lib/honeybadger/plugins/warden.rb +1 -0
- data/lib/honeybadger/rack/error_notifier.rb +11 -9
- data/lib/honeybadger/rack/user_feedback.rb +6 -4
- data/lib/honeybadger/rack/user_informer.rb +6 -4
- data/lib/honeybadger/ruby.rb +2 -0
- data/lib/honeybadger/singleton.rb +26 -0
- data/lib/honeybadger/util/http.rb +12 -0
- data/lib/honeybadger/util/request_hash.rb +71 -0
- data/lib/honeybadger/util/sanitizer.rb +101 -64
- data/lib/honeybadger/version.rb +1 -1
- data/lib/honeybadger/worker.rb +246 -0
- metadata +17 -13
- data/lib/honeybadger/agent/batch.rb +0 -50
- data/lib/honeybadger/agent/null_worker.rb +0 -26
- data/lib/honeybadger/agent/worker.rb +0 -243
- data/lib/honeybadger/cli/helpers.rb +0 -160
- data/lib/honeybadger/config/callbacks.rb +0 -70
- data/lib/honeybadger/plugins/unicorn.rb +0 -27
- data/lib/honeybadger/rack/metrics_reporter.rb +0 -16
- data/lib/honeybadger/rack/request_hash.rb +0 -55
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'delayed_job'
|
2
|
-
require 'honeybadger'
|
2
|
+
require 'honeybadger/ruby'
|
3
3
|
|
4
4
|
module Honeybadger
|
5
5
|
module Plugins
|
@@ -36,14 +36,13 @@ module Honeybadger
|
|
36
36
|
|
37
37
|
block.call(job)
|
38
38
|
rescue Exception => error
|
39
|
-
::Honeybadger.
|
39
|
+
::Honeybadger.notify(
|
40
40
|
:component => component,
|
41
41
|
:action => action,
|
42
42
|
:error_class => error.class.name,
|
43
43
|
:error_message => "#{ error.class.name }: #{ error.message }",
|
44
|
-
:backtrace => error.backtrace
|
45
|
-
|
46
|
-
) if job.attempts.to_i >= ::Honeybadger::Agent.config[:'delayed_job.attempt_threshold'].to_i
|
44
|
+
:backtrace => error.backtrace
|
45
|
+
) if job.attempts.to_i >= ::Honeybadger.config[:'delayed_job.attempt_threshold'].to_i
|
47
46
|
raise error
|
48
47
|
ensure
|
49
48
|
::Honeybadger.context.clear!
|
@@ -10,12 +10,11 @@ module Honeybadger
|
|
10
10
|
execution do
|
11
11
|
::PhusionPassenger.on_event(:starting_worker_process) do |forked|
|
12
12
|
logger.debug('Starting passenger worker process')
|
13
|
-
Honeybadger::Agent.fork if forked
|
14
13
|
end
|
15
14
|
|
16
15
|
::PhusionPassenger.on_event(:stopping_worker_process) do
|
17
16
|
logger.debug('Stopping passenger worker process')
|
18
|
-
Honeybadger
|
17
|
+
Honeybadger.stop
|
19
18
|
end
|
20
19
|
end
|
21
20
|
end
|
@@ -34,34 +34,6 @@ module Honeybadger
|
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
|
-
module ControllerMethods
|
38
|
-
def honeybadger_request_data
|
39
|
-
warn('#honeybadger_request_data has been deprecated and has no effect.')
|
40
|
-
{}
|
41
|
-
end
|
42
|
-
|
43
|
-
def notify_honeybadger(*args, &block)
|
44
|
-
warn('#notify_honeybadger has been deprecated; please use `Honeybadger.notify`.')
|
45
|
-
Honeybadger.notify(*args, &block)
|
46
|
-
end
|
47
|
-
|
48
|
-
def notify_honeybadger_or_ignore(*args, &block)
|
49
|
-
warn('#notify_honeybadger_or_ignore has been deprecated; please use `Honeybadger.notify`.')
|
50
|
-
Honeybadger.notify(*args, &block)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
Plugin.register :rails_controller_methods do
|
55
|
-
requirement { defined?(::Rails) }
|
56
|
-
|
57
|
-
execution do
|
58
|
-
ActiveSupport.on_load(:action_controller) do
|
59
|
-
# Lazily load action_controller methods
|
60
|
-
include ::Honeybadger::Plugins::Rails::ControllerMethods
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
37
|
Plugin.register :rails_exceptions_catcher do
|
66
38
|
requirement { defined?(::Rails) }
|
67
39
|
|
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'honeybadger/plugin'
|
2
|
-
require 'honeybadger'
|
2
|
+
require 'honeybadger/ruby'
|
3
3
|
|
4
4
|
module Honeybadger
|
5
5
|
module Plugins
|
@@ -20,7 +20,7 @@ module Honeybadger
|
|
20
20
|
|
21
21
|
def send_exception?(e, args)
|
22
22
|
return true unless respond_to?(:retry_criteria_valid?)
|
23
|
-
return true if ::Honeybadger
|
23
|
+
return true if ::Honeybadger.config[:'resque.resque_retry.send_exceptions_when_retrying']
|
24
24
|
|
25
25
|
!retry_criteria_valid?(e)
|
26
26
|
rescue => e
|
@@ -61,9 +61,6 @@ module Honeybadger
|
|
61
61
|
|
62
62
|
execution do
|
63
63
|
::Resque::Job.send(:include, Installer)
|
64
|
-
::Resque.after_fork do |job|
|
65
|
-
Honeybadger::Agent.fork
|
66
|
-
end
|
67
64
|
end
|
68
65
|
end
|
69
66
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'honeybadger/plugin'
|
2
|
-
require 'honeybadger'
|
2
|
+
require 'honeybadger/ruby'
|
3
3
|
|
4
4
|
module Honeybadger
|
5
5
|
module Plugins
|
@@ -16,7 +16,7 @@ module Honeybadger
|
|
16
16
|
yield
|
17
17
|
rescue => e
|
18
18
|
receive_count = sqs_msg.attributes['ApproximateReceiveCount'.freeze]
|
19
|
-
if receive_count && ::Honeybadger
|
19
|
+
if receive_count && ::Honeybadger.config[:'shoryuken.attempt_threshold'].to_i <= receive_count.to_i
|
20
20
|
Honeybadger.notify(e, parameters: body)
|
21
21
|
end
|
22
22
|
raise e
|
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'honeybadger/plugin'
|
2
|
-
require 'honeybadger'
|
2
|
+
require 'honeybadger/ruby'
|
3
3
|
|
4
4
|
module Honeybadger
|
5
5
|
module Plugins
|
@@ -27,7 +27,7 @@ module Honeybadger
|
|
27
27
|
return if params['retry'.freeze] && params['retry_count'.freeze].to_i < config[:'sidekiq.attempt_threshold'].to_i
|
28
28
|
opts = {parameters: params}
|
29
29
|
opts[:component] = params['wrapped'.freeze] || params['class'.freeze] if config[:'sidekiq.use_component']
|
30
|
-
Honeybadger.
|
30
|
+
Honeybadger.notify(ex, opts)
|
31
31
|
}
|
32
32
|
end
|
33
33
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'honeybadger/plugin'
|
2
|
-
require 'honeybadger'
|
2
|
+
require 'honeybadger/ruby'
|
3
3
|
|
4
4
|
module Honeybadger
|
5
5
|
module Plugins
|
@@ -16,7 +16,7 @@ module Honeybadger
|
|
16
16
|
def invoke_command_with_honeybadger(*args)
|
17
17
|
invoke_command_without_honeybadger(*args)
|
18
18
|
rescue Exception => e
|
19
|
-
Honeybadger.
|
19
|
+
Honeybadger.notify(e)
|
20
20
|
raise
|
21
21
|
end
|
22
22
|
end
|
@@ -1,6 +1,7 @@
|
|
1
|
-
require 'rack/request'
|
2
|
-
require 'honeybadger'
|
3
1
|
require 'forwardable'
|
2
|
+
require 'rack/request'
|
3
|
+
|
4
|
+
require 'honeybadger/ruby'
|
4
5
|
|
5
6
|
module Honeybadger
|
6
7
|
module Rack
|
@@ -21,13 +22,13 @@ module Honeybadger
|
|
21
22
|
class ErrorNotifier
|
22
23
|
extend Forwardable
|
23
24
|
|
24
|
-
def initialize(app,
|
25
|
+
def initialize(app, agent = nil)
|
25
26
|
@app = app
|
26
|
-
@
|
27
|
+
@agent = agent.kind_of?(Agent) ? agent : Honeybadger::Agent.instance
|
27
28
|
end
|
28
29
|
|
29
30
|
def call(env)
|
30
|
-
|
31
|
+
agent.with_rack_env(env) do
|
31
32
|
begin
|
32
33
|
env['honeybadger.config'] = config
|
33
34
|
response = @app.call(env)
|
@@ -44,13 +45,14 @@ module Honeybadger
|
|
44
45
|
response
|
45
46
|
end
|
46
47
|
ensure
|
47
|
-
|
48
|
+
agent.context.clear!
|
48
49
|
end
|
49
50
|
|
50
51
|
private
|
51
52
|
|
52
|
-
attr_reader :
|
53
|
-
def_delegator
|
53
|
+
attr_reader :agent
|
54
|
+
def_delegator :agent, :config
|
55
|
+
def_delegator :config, :logger
|
54
56
|
|
55
57
|
def ignored_user_agent?(env)
|
56
58
|
true if config[:'exceptions.ignored_user_agents'].
|
@@ -60,7 +62,7 @@ module Honeybadger
|
|
60
62
|
|
61
63
|
def notify_honeybadger(exception, env)
|
62
64
|
return if ignored_user_agent?(env)
|
63
|
-
|
65
|
+
agent.notify(exception)
|
64
66
|
end
|
65
67
|
|
66
68
|
def framework_exception(env)
|
@@ -19,12 +19,13 @@ module Honeybadger
|
|
19
19
|
class UserFeedback
|
20
20
|
extend Forwardable
|
21
21
|
|
22
|
-
def initialize(app,
|
22
|
+
def initialize(app, agent = nil)
|
23
23
|
@app = app
|
24
|
-
@
|
24
|
+
@agent = agent.kind_of?(Agent) ? agent : Honeybadger::Agent.instance
|
25
25
|
end
|
26
26
|
|
27
27
|
def call(env)
|
28
|
+
return @app.call(env) unless config[:'feedback.enabled']
|
28
29
|
status, headers, body = @app.call(env)
|
29
30
|
if env['honeybadger.error_id'] && form = render_form(env['honeybadger.error_id'])
|
30
31
|
new_body = []
|
@@ -67,8 +68,9 @@ module Honeybadger
|
|
67
68
|
|
68
69
|
private
|
69
70
|
|
70
|
-
attr_reader :
|
71
|
-
def_delegator
|
71
|
+
attr_reader :agent
|
72
|
+
def_delegator :agent, :config
|
73
|
+
def_delegator :config, :logger
|
72
74
|
end
|
73
75
|
end
|
74
76
|
end
|
@@ -5,9 +5,9 @@ module Honeybadger
|
|
5
5
|
class UserInformer
|
6
6
|
extend Forwardable
|
7
7
|
|
8
|
-
def initialize(app,
|
8
|
+
def initialize(app, agent = nil)
|
9
9
|
@app = app
|
10
|
-
@
|
10
|
+
@agent = agent.kind_of?(Agent) ? agent : Honeybadger::Agent.instance
|
11
11
|
end
|
12
12
|
|
13
13
|
def replacement(with)
|
@@ -15,6 +15,7 @@ module Honeybadger
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def call(env)
|
18
|
+
return @app.call(env) unless config[:'user_informer.enabled']
|
18
19
|
status, headers, body = @app.call(env)
|
19
20
|
if env['honeybadger.error_id']
|
20
21
|
new_body = []
|
@@ -31,8 +32,9 @@ module Honeybadger
|
|
31
32
|
|
32
33
|
private
|
33
34
|
|
34
|
-
attr_reader :
|
35
|
-
def_delegator
|
35
|
+
attr_reader :agent
|
36
|
+
def_delegator :agent, :config
|
37
|
+
def_delegator :config, :logger
|
36
38
|
end
|
37
39
|
end
|
38
40
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'honeybadger/agent'
|
3
|
+
|
4
|
+
# The Singleton module includes the public API for Honeybadger which can be
|
5
|
+
# accessed via the global agent (i.e. `Honeybadger.notify`) or via instances of
|
6
|
+
# the `Honeybadger::Agent` class.
|
7
|
+
module Honeybadger
|
8
|
+
extend Forwardable
|
9
|
+
extend self
|
10
|
+
|
11
|
+
def_delegators :'Agent.instance', :init!, :config, :configure, :notify,
|
12
|
+
:context, :get_context, :flush, :stop, :with_rack_env, :exception_filter,
|
13
|
+
:exception_fingerprint, :backtrace_filter
|
14
|
+
|
15
|
+
def load_plugins!
|
16
|
+
Dir[File.expand_path('../plugins/*.rb', __FILE__)].each do |plugin|
|
17
|
+
require plugin
|
18
|
+
end
|
19
|
+
Plugin.load!(self.config)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Deprecated
|
23
|
+
def start(config = {})
|
24
|
+
true
|
25
|
+
end
|
26
|
+
end
|
@@ -21,6 +21,18 @@ module Honeybadger
|
|
21
21
|
'User-Agent'.freeze => "HB-Ruby #{VERSION}; #{RUBY_VERSION}; #{RUBY_PLATFORM}".freeze
|
22
22
|
}.freeze
|
23
23
|
|
24
|
+
ERRORS = [Timeout::Error,
|
25
|
+
Errno::EINVAL,
|
26
|
+
Errno::ECONNRESET,
|
27
|
+
Errno::ECONNREFUSED,
|
28
|
+
Errno::ENETUNREACH,
|
29
|
+
EOFError,
|
30
|
+
Net::HTTPBadResponse,
|
31
|
+
Net::HTTPHeaderSyntaxError,
|
32
|
+
Net::ProtocolError,
|
33
|
+
OpenSSL::SSL::SSLError,
|
34
|
+
SocketError].freeze
|
35
|
+
|
24
36
|
def initialize(config)
|
25
37
|
@config = config
|
26
38
|
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module Honeybadger
|
4
|
+
module Util
|
5
|
+
# Internal: Constructs a request hash from a Rack::Request matching the
|
6
|
+
# /v1/notices API specification.
|
7
|
+
module RequestHash
|
8
|
+
HTTP_HEADER_PREFIX = 'HTTP_'.freeze
|
9
|
+
|
10
|
+
CGI_WHITELIST = %w(
|
11
|
+
AUTH_TYPE
|
12
|
+
CONTENT_LENGTH
|
13
|
+
CONTENT_TYPE
|
14
|
+
GATEWAY_INTERFACE
|
15
|
+
HTTPS
|
16
|
+
REMOTE_ADDR
|
17
|
+
REMOTE_HOST
|
18
|
+
REMOTE_IDENT
|
19
|
+
REMOTE_USER
|
20
|
+
REQUEST_METHOD
|
21
|
+
SERVER_NAME
|
22
|
+
SERVER_PORT
|
23
|
+
SERVER_PROTOCOL
|
24
|
+
SERVER_SOFTWARE
|
25
|
+
).freeze
|
26
|
+
|
27
|
+
def self.from_env(env)
|
28
|
+
return {} unless defined?(::Rack::Request)
|
29
|
+
|
30
|
+
hash, request = {}, ::Rack::Request.new(env)
|
31
|
+
|
32
|
+
hash[:url] = extract_url(request)
|
33
|
+
hash[:params] = extract_params(request)
|
34
|
+
hash[:component] = hash[:params]['controller']
|
35
|
+
hash[:action] = hash[:params]['action']
|
36
|
+
hash[:session] = extract_session(request)
|
37
|
+
hash[:cgi_data] = extract_cgi_data(request)
|
38
|
+
|
39
|
+
hash
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.extract_url(request)
|
43
|
+
request.env['honeybadger.request.url'] || request.url
|
44
|
+
rescue => e
|
45
|
+
"Failed to access URL -- #{e}"
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.extract_params(request)
|
49
|
+
(request.env['action_dispatch.request.parameters'] || request.params).to_hash || {}
|
50
|
+
rescue => e
|
51
|
+
{ error: "Failed to access params -- #{e}" }
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.extract_session(request)
|
55
|
+
request.session.to_hash
|
56
|
+
rescue => e
|
57
|
+
# Rails raises ArgumentError when `config.secret_token` is missing, and
|
58
|
+
# ActionDispatch::Session::SessionRestoreError when the session can't be
|
59
|
+
# restored.
|
60
|
+
{ error: "Failed to access session data -- #{e}" }
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.extract_cgi_data(request)
|
64
|
+
request.env.each_with_object({}) do |(k,v), env|
|
65
|
+
next unless k.start_with?(HTTP_HEADER_PREFIX) || CGI_WHITELIST.include?(k)
|
66
|
+
env[k] = v
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -1,133 +1,170 @@
|
|
1
|
+
require 'bigdecimal'
|
1
2
|
require 'set'
|
2
3
|
|
3
4
|
module Honeybadger
|
4
5
|
module Util
|
6
|
+
# Internal: Sanitizer sanitizes data for sending to Honeybadger's API. The
|
7
|
+
# filters are based on Rails' HTTP parameter filter.
|
5
8
|
class Sanitizer
|
6
|
-
|
9
|
+
COOKIE_PAIRS = /[;,]\s?/
|
10
|
+
COOKIE_SEP = '='.freeze
|
11
|
+
COOKIE_PAIR_SEP = '; '.freeze
|
7
12
|
|
8
|
-
|
13
|
+
ENCODE_OPTS = { invalid: :replace, undef: :replace, replace: '?'.freeze }.freeze
|
14
|
+
|
15
|
+
FILTERED = '[FILTERED]'.freeze
|
16
|
+
|
17
|
+
IMMUTABLE = [NilClass, FalseClass, TrueClass, Symbol, Numeric, BigDecimal, Method].freeze
|
9
18
|
|
10
19
|
MAX_STRING_SIZE = 2048
|
11
20
|
|
12
|
-
|
13
|
-
|
14
|
-
|
21
|
+
TRUNCATION_REPLACEMENT = '[TRUNCATED]'.freeze
|
22
|
+
|
23
|
+
VALID_ENCODINGS = [Encoding::UTF_8, Encoding::ISO_8859_1].freeze
|
24
|
+
|
25
|
+
def self.sanitize(data)
|
26
|
+
@sanitizer ||= new
|
27
|
+
@sanitizer.sanitize(data)
|
28
|
+
end
|
15
29
|
|
16
|
-
def initialize(
|
17
|
-
@
|
30
|
+
def initialize(max_depth: 20, filters: [])
|
31
|
+
@filters = !filters.empty?
|
32
|
+
@max_depth = max_depth
|
18
33
|
|
19
|
-
|
20
|
-
|
21
|
-
|
34
|
+
strings, @regexps, @blocks = [], [], []
|
35
|
+
|
36
|
+
filters.each do |item|
|
37
|
+
case item
|
38
|
+
when Proc
|
39
|
+
@blocks << item
|
40
|
+
when Regexp
|
41
|
+
@regexps << item
|
42
|
+
else
|
43
|
+
strings << Regexp.escape(item.to_s)
|
22
44
|
end
|
23
45
|
end
|
46
|
+
|
47
|
+
@deep_regexps, @regexps = @regexps.partition { |r| r.to_s.include?('\\.'.freeze) }
|
48
|
+
deep_strings, @strings = strings.partition { |s| s.include?('\\.'.freeze) }
|
49
|
+
|
50
|
+
@regexps << Regexp.new(strings.join('|'.freeze), true) unless strings.empty?
|
51
|
+
@deep_regexps << Regexp.new(deep_strings.join('|'.freeze), true) unless deep_strings.empty?
|
24
52
|
end
|
25
53
|
|
26
|
-
def sanitize(data, depth = 0, stack = nil)
|
27
|
-
if
|
28
|
-
return '[possible infinite recursion halted]' if stack && stack.include?(data.object_id)
|
54
|
+
def sanitize(data, depth = 0, stack = nil, parents = [])
|
55
|
+
if enumerable?(data)
|
56
|
+
return '[possible infinite recursion halted]'.freeze if stack && stack.include?(data.object_id)
|
29
57
|
stack = stack ? stack.dup : Set.new
|
30
58
|
stack << data.object_id
|
31
59
|
end
|
32
60
|
|
33
61
|
case data
|
34
62
|
when Hash
|
35
|
-
return '[max depth reached]' if depth >= max_depth
|
63
|
+
return '[max depth reached]'.freeze if depth >= max_depth
|
36
64
|
hash = data.to_hash
|
37
65
|
new_hash = {}
|
38
66
|
hash.each_pair do |key, value|
|
39
|
-
|
40
|
-
|
41
|
-
|
67
|
+
parents.push(key) if deep_regexps
|
68
|
+
key = key.kind_of?(Symbol) ? key : sanitize(key, depth+1, stack, parents)
|
69
|
+
if filter_key?(key, parents)
|
70
|
+
new_hash[key] = FILTERED
|
42
71
|
else
|
43
|
-
|
72
|
+
value = sanitize(value, depth+1, stack, parents)
|
73
|
+
if blocks.any? && !enumerable?(value)
|
74
|
+
key = key.dup if can_dup?(key)
|
75
|
+
value = value.dup if can_dup?(value)
|
76
|
+
blocks.each { |b| b.call(key, value) }
|
77
|
+
end
|
78
|
+
new_hash[key] = value
|
44
79
|
end
|
80
|
+
parents.pop if deep_regexps
|
45
81
|
end
|
46
82
|
new_hash
|
47
83
|
when Array, Set
|
48
|
-
return '[max depth reached]' if depth >= max_depth
|
84
|
+
return '[max depth reached]'.freeze if depth >= max_depth
|
49
85
|
data.to_a.map do |value|
|
50
|
-
sanitize(value, depth+1, stack)
|
86
|
+
sanitize(value, depth+1, stack, parents)
|
51
87
|
end.compact
|
52
88
|
when Numeric, TrueClass, FalseClass, NilClass
|
53
89
|
data
|
54
90
|
when String
|
55
|
-
|
91
|
+
sanitize_string(data)
|
56
92
|
else # all other objects:
|
57
|
-
|
93
|
+
data.respond_to?(:to_s) ? sanitize_string(data.to_s) : nil
|
58
94
|
end
|
59
95
|
end
|
60
96
|
|
97
|
+
def sanitize_string(string)
|
98
|
+
string = valid_encoding(string.to_s)
|
99
|
+
return string unless string.respond_to?(:size) && string.size > MAX_STRING_SIZE
|
100
|
+
string[0...MAX_STRING_SIZE] + TRUNCATION_REPLACEMENT
|
101
|
+
end
|
102
|
+
|
61
103
|
def filter_cookies(raw_cookies)
|
62
|
-
return raw_cookies unless filters
|
104
|
+
return raw_cookies unless filters?
|
63
105
|
|
64
106
|
cookies = []
|
65
|
-
|
107
|
+
|
108
|
+
raw_cookies.to_s.split(COOKIE_PAIRS).each do |pair|
|
66
109
|
name, values = pair.split(COOKIE_SEP, 2)
|
67
|
-
values =
|
68
|
-
cookies << "#{
|
110
|
+
values = FILTERED if filter_key?(name)
|
111
|
+
cookies << "#{name}=#{values}"
|
69
112
|
end
|
70
113
|
|
71
114
|
cookies.join(COOKIE_PAIR_SEP)
|
72
115
|
end
|
73
116
|
|
74
117
|
def filter_url(url)
|
75
|
-
return url unless filters
|
118
|
+
return url unless filters?
|
76
119
|
|
77
120
|
filtered_url = url.to_s.dup
|
121
|
+
|
78
122
|
filtered_url.scan(/(?:^|&|\?)([^=?&]+)=([^&]+)/).each do |m|
|
79
123
|
next unless filter_key?(m[0])
|
80
|
-
filtered_url.gsub!(/#{Regexp.escape(m[1])}/,
|
124
|
+
filtered_url.gsub!(/#{Regexp.escape(m[1])}/, FILTERED)
|
81
125
|
end
|
82
126
|
|
83
127
|
filtered_url
|
84
128
|
end
|
85
129
|
|
86
|
-
|
87
|
-
ENCODE_OPTS = { invalid: :replace, undef: :replace, replace: '?'.freeze }.freeze
|
88
|
-
UTF8_STRING = ''.freeze
|
89
|
-
|
90
|
-
class << self
|
91
|
-
|
92
|
-
def valid_encoding?(data)
|
93
|
-
data.valid_encoding? && (
|
94
|
-
VALID_ENCODINGS.include?(data.encoding) ||
|
95
|
-
VALID_ENCODINGS.include?(Encoding.compatible?(UTF8_STRING, data))
|
96
|
-
)
|
97
|
-
end
|
130
|
+
private
|
98
131
|
|
99
|
-
|
100
|
-
return data if valid_encoding?(data)
|
132
|
+
attr_reader :max_depth, :regexps, :deep_regexps, :blocks
|
101
133
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
data.encode(Encoding::UTF_8, ENCODE_OPTS)
|
106
|
-
end
|
107
|
-
end
|
134
|
+
def filters?
|
135
|
+
!!@filters
|
136
|
+
end
|
108
137
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
138
|
+
def filter_key?(key, parents = nil)
|
139
|
+
return false unless filters?
|
140
|
+
return true if regexps.any? { |r| key =~ r }
|
141
|
+
return true if deep_regexps && parents && (joined = parents.join(".")) && deep_regexps.any? { |r| joined =~ r }
|
142
|
+
false
|
143
|
+
end
|
114
144
|
|
145
|
+
def valid_encoding?(string)
|
146
|
+
string.valid_encoding? && (
|
147
|
+
VALID_ENCODINGS.include?(string.encoding) ||
|
148
|
+
VALID_ENCODINGS.include?(Encoding.compatible?(''.freeze, string))
|
149
|
+
)
|
115
150
|
end
|
116
151
|
|
117
|
-
|
152
|
+
def valid_encoding(string)
|
153
|
+
return string if valid_encoding?(string)
|
118
154
|
|
119
|
-
|
155
|
+
if string.encoding == Encoding::UTF_8
|
156
|
+
string.encode(Encoding::UTF_16, ENCODE_OPTS).encode!(Encoding::UTF_8)
|
157
|
+
else
|
158
|
+
string.encode(Encoding::UTF_8, ENCODE_OPTS)
|
159
|
+
end
|
160
|
+
end
|
120
161
|
|
121
|
-
def
|
122
|
-
|
162
|
+
def enumerable?(data)
|
163
|
+
data.kind_of?(Hash) || data.kind_of?(Array) || data.kind_of?(Set)
|
164
|
+
end
|
123
165
|
|
124
|
-
|
125
|
-
|
126
|
-
filter =~ key.to_s
|
127
|
-
else
|
128
|
-
key.to_s.eql?(filter.to_s)
|
129
|
-
end
|
130
|
-
end
|
166
|
+
def can_dup?(obj)
|
167
|
+
!IMMUTABLE.any? {|k| obj.kind_of?(k) }
|
131
168
|
end
|
132
169
|
end
|
133
170
|
end
|