honeybadger 2.7.2 → 3.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|