bugsnag 6.12.1 → 6.15.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (141) hide show
  1. checksums.yaml +4 -4
  2. data/.buildkite/pipeline.yml +470 -0
  3. data/.rubocop.yml +55 -0
  4. data/.rubocop_todo.yml +530 -160
  5. data/CHANGELOG.md +73 -0
  6. data/CONTRIBUTING.md +1 -9
  7. data/Gemfile +14 -7
  8. data/TESTING.md +81 -0
  9. data/VERSION +1 -1
  10. data/docker-compose.yml +46 -0
  11. data/dockerfiles/Dockerfile.jruby-unit-tests +13 -0
  12. data/dockerfiles/Dockerfile.ruby-maze-runner +26 -0
  13. data/dockerfiles/Dockerfile.ruby-unit-tests +12 -0
  14. data/features/delayed_job.feature +6 -22
  15. data/features/fixtures/delayed_job/Dockerfile +2 -4
  16. data/features/fixtures/delayed_job/app/Gemfile +1 -1
  17. data/features/fixtures/delayed_job/app/Rakefile +18 -0
  18. data/features/fixtures/docker-compose.yml +32 -40
  19. data/features/fixtures/expected_breadcrumbs/active_job.json +9 -0
  20. data/features/fixtures/expected_breadcrumbs/mongo_failed.json +15 -0
  21. data/features/fixtures/expected_breadcrumbs/mongo_filtered_request.json +15 -0
  22. data/features/fixtures/expected_breadcrumbs/mongo_filtered_result.json +15 -0
  23. data/features/fixtures/expected_breadcrumbs/mongo_success.json +14 -0
  24. data/features/fixtures/expected_breadcrumbs/request.json +13 -0
  25. data/features/fixtures/expected_breadcrumbs/sql_with_bindings.json +12 -0
  26. data/features/fixtures/expected_breadcrumbs/sql_without_bindings.json +11 -0
  27. data/features/fixtures/plain/Dockerfile +2 -2
  28. data/features/fixtures/plain/app/app.rb +1 -3
  29. data/features/fixtures/plain/app/delivery/fork_threadpool.rb +3 -1
  30. data/features/fixtures/plain/app/report_modification/initiators/handled_on_error.rb +10 -0
  31. data/features/fixtures/plain/app/report_modification/initiators/unhandled_on_error.rb +11 -0
  32. data/features/fixtures/plain/app/stack_frame_modification/initiators/handled_on_error.rb +29 -0
  33. data/features/fixtures/plain/app/stack_frame_modification/initiators/unhandled_on_error.rb +26 -0
  34. data/features/fixtures/plain/app/unhandled/{Interrupt.rb → interrupt.rb} +0 -0
  35. data/features/fixtures/rack1/Dockerfile +2 -2
  36. data/features/fixtures/rack2/Dockerfile +2 -2
  37. data/features/fixtures/rails3/Dockerfile +2 -2
  38. data/features/fixtures/rails3/app/Gemfile +4 -0
  39. data/features/fixtures/rails3/app/config/initializers/bugsnag.rb +11 -2
  40. data/features/fixtures/rails4/Dockerfile +2 -5
  41. data/features/fixtures/rails4/app/config/initializers/bugsnag.rb +10 -1
  42. data/features/fixtures/rails5/Dockerfile +2 -2
  43. data/features/fixtures/rails5/app/Gemfile +3 -2
  44. data/features/fixtures/rails5/app/config/initializers/bugsnag.rb +10 -1
  45. data/features/fixtures/rails6/Dockerfile +2 -2
  46. data/features/fixtures/rails6/app/Gemfile +3 -2
  47. data/features/fixtures/rails6/app/app/controllers/mongo_controller.rb +22 -0
  48. data/features/fixtures/rails6/app/app/models/mongo_model.rb +6 -0
  49. data/features/fixtures/rails6/app/config/environments/development.rb +2 -0
  50. data/features/fixtures/rails6/app/config/environments/production.rb +1 -0
  51. data/features/fixtures/rails6/app/config/environments/rails_env.rb +1 -0
  52. data/features/fixtures/rails6/app/config/environments/test.rb +1 -0
  53. data/features/fixtures/rails6/app/config/initializers/bugsnag.rb +10 -1
  54. data/features/fixtures/rails6/app/config/mongoid.yml +23 -0
  55. data/features/fixtures/rails6/app/config/routes.rb +4 -0
  56. data/features/fixtures/resque/Dockerfile +2 -2
  57. data/features/fixtures/sidekiq/Dockerfile +5 -7
  58. data/features/fixtures/sidekiq/app/Gemfile +2 -1
  59. data/features/fixtures/sidekiq/app/Rakefile.rb +14 -0
  60. data/features/fixtures/sinatra1/Dockerfile +2 -2
  61. data/features/fixtures/sinatra2/Dockerfile +2 -2
  62. data/features/plain_features/add_tab.feature +30 -97
  63. data/features/plain_features/app_type.feature +6 -25
  64. data/features/plain_features/app_version.feature +6 -25
  65. data/features/plain_features/auto_notify.feature +4 -20
  66. data/features/plain_features/delivery.feature +12 -60
  67. data/features/plain_features/exception_data.feature +24 -94
  68. data/features/plain_features/filters.feature +9 -43
  69. data/features/plain_features/handled_errors.feature +16 -78
  70. data/features/plain_features/ignore_classes.feature +5 -23
  71. data/features/plain_features/ignore_report.feature +8 -24
  72. data/features/plain_features/proxies.feature +13 -56
  73. data/features/plain_features/release_stages.feature +9 -40
  74. data/features/plain_features/report_api_key.feature +11 -35
  75. data/features/plain_features/report_severity.feature +10 -35
  76. data/features/plain_features/report_stack_frames.feature +29 -93
  77. data/features/plain_features/report_user.feature +29 -96
  78. data/features/plain_features/unhandled_errors.feature +17 -88
  79. data/features/rails_features/api_key.feature +12 -58
  80. data/features/rails_features/app_type.feature +13 -58
  81. data/features/rails_features/app_version.feature +19 -80
  82. data/features/rails_features/auto_capture_sessions.feature +31 -112
  83. data/features/rails_features/auto_notify.feature +28 -105
  84. data/features/rails_features/before_notify.feature +18 -83
  85. data/features/rails_features/breadcrumbs.feature +40 -137
  86. data/features/rails_features/handled.feature +18 -82
  87. data/features/rails_features/ignore_classes.feature +12 -51
  88. data/features/rails_features/meta_data_filters.feature +9 -33
  89. data/features/rails_features/mongo_breadcrumbs.feature +22 -96
  90. data/features/rails_features/on_error.feature +29 -0
  91. data/features/rails_features/project_root.feature +19 -84
  92. data/features/rails_features/release_stage.feature +20 -82
  93. data/features/rails_features/send_code.feature +13 -55
  94. data/features/rails_features/send_environment.feature +7 -33
  95. data/features/rails_features/unhandled.feature +6 -31
  96. data/features/rails_features/user_info.feature +27 -65
  97. data/features/sidekiq.feature +12 -79
  98. data/features/steps/ruby_notifier_steps.rb +59 -15
  99. data/features/support/env.rb +12 -45
  100. data/lib/bugsnag.rb +109 -21
  101. data/lib/bugsnag/breadcrumbs/breadcrumbs.rb +0 -2
  102. data/lib/bugsnag/breadcrumbs/validator.rb +0 -6
  103. data/lib/bugsnag/cleaner.rb +129 -60
  104. data/lib/bugsnag/code_extractor.rb +137 -0
  105. data/lib/bugsnag/configuration.rb +58 -1
  106. data/lib/bugsnag/helpers.rb +2 -4
  107. data/lib/bugsnag/integrations/que.rb +7 -4
  108. data/lib/bugsnag/middleware/discard_error_class.rb +30 -0
  109. data/lib/bugsnag/middleware/exception_meta_data.rb +15 -9
  110. data/lib/bugsnag/middleware/ignore_error_class.rb +2 -0
  111. data/lib/bugsnag/middleware/rack_request.rb +2 -4
  112. data/lib/bugsnag/middleware_stack.rb +38 -3
  113. data/lib/bugsnag/on_error_callbacks.rb +33 -0
  114. data/lib/bugsnag/report.rb +4 -14
  115. data/lib/bugsnag/session_tracker.rb +3 -3
  116. data/lib/bugsnag/stacktrace.rb +28 -75
  117. data/spec/breadcrumbs/breadcrumb_spec.rb +1 -1
  118. data/spec/breadcrumbs/validator_spec.rb +1 -26
  119. data/spec/bugsnag_spec.rb +2 -2
  120. data/spec/cleaner_spec.rb +202 -10
  121. data/spec/code_extractor_spec.rb +129 -0
  122. data/spec/configuration_spec.rb +16 -1
  123. data/spec/fixtures/apps/rails-initializer-config/Gemfile +5 -1
  124. data/spec/fixtures/apps/rails-invalid-initializer-config/Gemfile +5 -1
  125. data/spec/fixtures/apps/rails-no-config/Gemfile +5 -1
  126. data/spec/fixtures/crashes/file1.rb +29 -0
  127. data/spec/fixtures/crashes/file2.rb +25 -0
  128. data/spec/fixtures/crashes/file_with_long_lines.rb +7 -0
  129. data/spec/fixtures/crashes/functions.rb +29 -0
  130. data/spec/fixtures/crashes/short_file.rb +2 -0
  131. data/spec/helper_spec.rb +0 -31
  132. data/spec/integrations/logger_spec.rb +1 -1
  133. data/spec/integrations/rack_spec.rb +8 -6
  134. data/spec/integrations/rake_spec.rb +1 -1
  135. data/spec/on_error_spec.rb +332 -0
  136. data/spec/report_spec.rb +331 -30
  137. data/spec/spec_helper.rb +14 -1
  138. data/spec/stacktrace_spec.rb +427 -74
  139. metadata +36 -7
  140. data/.travis.yml +0 -117
  141. data/features/plain_features/api_key.feature +0 -25
@@ -0,0 +1,33 @@
1
+ module Bugsnag
2
+ # @api private
3
+ class OnErrorCallbacks
4
+ def initialize(next_middleware, callbacks)
5
+ @next_middleware = next_middleware
6
+ @callbacks = callbacks
7
+ end
8
+
9
+ ##
10
+ # @param report [Report]
11
+ def call(report)
12
+ @callbacks.each do |callback|
13
+ begin
14
+ should_continue = callback.call(report)
15
+ rescue StandardError => e
16
+ Bugsnag.configuration.warn("Error occurred in on_error callback: '#{e}'")
17
+ Bugsnag.configuration.warn("on_error callback stacktrace: #{e.backtrace.inspect}")
18
+ end
19
+
20
+ # If a callback returns false, we ignore the report and stop running callbacks
21
+ # Note that we explicitly check for 'false' so that callbacks don't need
22
+ # to return anything (i.e. can return 'nil') and we still continue
23
+ next unless should_continue == false
24
+
25
+ report.ignore!
26
+
27
+ break
28
+ end
29
+
30
+ @next_middleware.call(report)
31
+ end
32
+ end
33
+ end
@@ -97,6 +97,7 @@ module Bugsnag
97
97
  releaseStage: release_stage,
98
98
  type: app_type
99
99
  },
100
+ breadcrumbs: breadcrumbs.map(&:to_h),
100
101
  context: context,
101
102
  device: {
102
103
  hostname: hostname,
@@ -104,6 +105,7 @@ module Bugsnag
104
105
  },
105
106
  exceptions: exceptions,
106
107
  groupingHash: grouping_hash,
108
+ metaData: meta_data,
107
109
  session: session,
108
110
  severity: severity,
109
111
  severityReason: severity_reason,
@@ -111,19 +113,7 @@ module Bugsnag
111
113
  user: user
112
114
  }
113
115
 
114
- # cleanup character encodings
115
- payload_event = Bugsnag::Cleaner.clean_object_encoding(payload_event)
116
-
117
- # filter out sensitive values in (and cleanup encodings) metaData
118
- filter_cleaner = Bugsnag::Cleaner.new(configuration.meta_data_filters)
119
- payload_event[:metaData] = filter_cleaner.clean_object(meta_data)
120
- payload_event[:breadcrumbs] = breadcrumbs.map do |breadcrumb|
121
- breadcrumb_hash = breadcrumb.to_h
122
- breadcrumb_hash[:metaData] = filter_cleaner.clean_object(breadcrumb_hash[:metaData])
123
- breadcrumb_hash
124
- end
125
-
126
- payload_event.reject! {|k,v| v.nil? }
116
+ payload_event.reject! {|k, v| v.nil? }
127
117
 
128
118
  # return the payload hash
129
119
  {
@@ -192,7 +182,7 @@ module Bugsnag
192
182
  {
193
183
  errorClass: error_class(exception),
194
184
  message: exception.message,
195
- stacktrace: Stacktrace.new(exception.backtrace, configuration).to_a
185
+ stacktrace: Stacktrace.process(exception.backtrace, configuration)
196
186
  }
197
187
  end
198
188
  end
@@ -1,11 +1,9 @@
1
1
  require 'thread'
2
2
  require 'time'
3
3
  require 'securerandom'
4
- require 'concurrent'
5
4
 
6
5
  module Bugsnag
7
6
  class SessionTracker
8
-
9
7
  THREAD_SESSION = "bugsnag_session"
10
8
  SESSION_PAYLOAD_VERSION = "1.0"
11
9
  MUTEX = Mutex.new
@@ -27,6 +25,8 @@ module Bugsnag
27
25
  ##
28
26
  # Initializes the session tracker.
29
27
  def initialize
28
+ require 'concurrent'
29
+
30
30
  @session_counts = Concurrent::Hash.new(0)
31
31
  end
32
32
 
@@ -139,4 +139,4 @@ module Bugsnag
139
139
  Bugsnag::Delivery[Bugsnag.configuration.delivery_method].deliver(Bugsnag.configuration.session_endpoint, payload, Bugsnag.configuration, options)
140
140
  end
141
141
  end
142
- end
142
+ end
@@ -1,55 +1,51 @@
1
- module Bugsnag
2
- class Stacktrace
1
+ require_relative 'code_extractor'
3
2
 
3
+ module Bugsnag
4
+ module Stacktrace
4
5
  # e.g. "org/jruby/RubyKernel.java:1264:in `catch'"
5
6
  BACKTRACE_LINE_REGEX = /^((?:[a-zA-Z]:)?[^:]+):(\d+)(?::in `([^']+)')?$/
6
7
 
7
8
  # e.g. "org.jruby.Ruby.runScript(Ruby.java:807)"
8
9
  JAVA_BACKTRACE_REGEX = /^(.*)\((.*)(?::([0-9]+))?\)$/
9
10
 
10
- # Path to vendored code. Used to mark file paths as out of project.
11
- VENDOR_PATH = /^(vendor\/|\.bundle\/)/
12
-
13
11
  ##
14
12
  # Process a backtrace and the configuration into a parsed stacktrace.
15
- def initialize(backtrace, configuration)
16
- @configuration = configuration
13
+ #
14
+ # @param backtrace [Array, nil] If nil, 'caller' will be used instead
15
+ # @param configuration [Configuration]
16
+ # @return [Array]
17
+ def self.process(backtrace, configuration)
18
+ code_extractor = CodeExtractor.new(configuration)
17
19
 
18
20
  backtrace = caller if !backtrace || backtrace.empty?
19
21
 
20
- @processed_backtrace = backtrace.map do |trace|
22
+ processed_backtrace = backtrace.map do |trace|
23
+ # Parse the stacktrace line
21
24
  if trace.match(BACKTRACE_LINE_REGEX)
22
25
  file, line_str, method = [$1, $2, $3]
23
26
  elsif trace.match(JAVA_BACKTRACE_REGEX)
24
27
  method, file, line_str = [$1, $2, $3]
25
28
  end
26
29
 
27
- # Parse the stacktrace line
28
-
29
- next(nil) if file.nil?
30
+ next if file.nil?
30
31
 
31
32
  # Expand relative paths
32
- p = Pathname.new(file)
33
- if p.relative?
34
- file = p.realpath.to_s rescue file
35
- end
33
+ file = File.realpath(file) rescue file
36
34
 
37
35
  # Generate the stacktrace line hash
38
- trace_hash = {}
39
- trace_hash[:lineNumber] = line_str.to_i
36
+ trace_hash = { lineNumber: line_str.to_i }
40
37
 
41
- if configuration.send_code
42
- trace_hash[:code] = code(file, trace_hash[:lineNumber])
43
- end
38
+ # Save a copy of the file path as we're about to modify it but need the
39
+ # raw version when extracting code (otherwise we can't open the file)
40
+ raw_file_path = file.dup
44
41
 
45
42
  # Clean up the file path in the stacktrace
46
- if defined?(@configuration.project_root) && @configuration.project_root.to_s != ''
47
- trace_hash[:inProject] = true if file.start_with?(@configuration.project_root.to_s)
48
- file.sub!(/#{@configuration.project_root}\//, "")
49
- trace_hash.delete(:inProject) if file.match(VENDOR_PATH)
43
+ if defined?(configuration.project_root) && configuration.project_root.to_s != ''
44
+ trace_hash[:inProject] = true if file.start_with?(configuration.project_root.to_s)
45
+ file.sub!(/#{configuration.project_root}\//, "")
46
+ trace_hash.delete(:inProject) if file.match(configuration.vendor_path)
50
47
  end
51
48
 
52
-
53
49
  # Strip common gem path prefixes
54
50
  if defined?(Gem)
55
51
  file = Gem.path.inject(file) {|line, path| line.sub(/#{path}\//, "") }
@@ -60,59 +56,16 @@ module Bugsnag
60
56
  # Add a method if we have it
61
57
  trace_hash[:method] = method if method && (method =~ /^__bind/).nil?
62
58
 
63
- if trace_hash[:file] && !trace_hash[:file].empty?
64
- trace_hash
65
- else
66
- nil
67
- end
68
- end.compact
69
- end
70
-
71
- ##
72
- # Returns the processed backtrace
73
- def to_a
74
- @processed_backtrace
75
- end
76
-
77
- private
78
-
79
- def code(file, line_number, num_lines = 7)
80
- code_hash = {}
81
-
82
- from_line = [line_number - num_lines, 1].max
83
-
84
- # don't try and open '(irb)' or '-e'
85
- return unless File.exist?(file)
86
-
87
- # Populate code hash with line numbers and code lines
88
- File.open(file) do |f|
89
- current_line_number = 0
90
- f.each_line do |line|
91
- current_line_number += 1
59
+ # If we're going to send code then record the raw file path and the
60
+ # trace_hash, so we can extract from it later
61
+ code_extractor.add_file(raw_file_path, trace_hash) if configuration.send_code
92
62
 
93
- next if current_line_number < from_line
94
-
95
- code_hash[current_line_number] = line[0...200].rstrip
96
-
97
- break if code_hash.length >= ( num_lines * 1.5 ).ceil
98
- end
99
- end
100
-
101
- while code_hash.length > num_lines
102
- last_line = code_hash.keys.max
103
- first_line = code_hash.keys.min
63
+ trace_hash
64
+ end.compact
104
65
 
105
- if (last_line - line_number) > (line_number - first_line)
106
- code_hash.delete(last_line)
107
- else
108
- code_hash.delete(first_line)
109
- end
110
- end
66
+ code_extractor.extract! if configuration.send_code
111
67
 
112
- code_hash
113
- rescue
114
- @configuration.warn("Error fetching code: #{$!.inspect}")
115
- nil
68
+ processed_backtrace
116
69
  end
117
70
  end
118
71
  end
@@ -90,4 +90,4 @@ RSpec.describe Bugsnag::Breadcrumbs::Breadcrumb do
90
90
  )
91
91
  end
92
92
  end
93
- end
93
+ end
@@ -31,31 +31,6 @@ RSpec.describe Bugsnag::Breadcrumbs::Validator do
31
31
  validator.validate(breadcrumb)
32
32
  end
33
33
 
34
- it "trims long messages to length and warns" do
35
- config = instance_double(Bugsnag::Configuration)
36
- allow(config).to receive(:enabled_automatic_breadcrumb_types).and_return(enabled_automatic_breadcrumb_types)
37
- validator = Bugsnag::Breadcrumbs::Validator.new(config)
38
-
39
- name = "1234567890123456789012345678901234567890"
40
-
41
- breadcrumb = instance_double(Bugsnag::Breadcrumbs::Breadcrumb, {
42
- :auto => auto,
43
- :name => name,
44
- :type => type,
45
- :meta_data => meta_data,
46
- :meta_data= => nil
47
- })
48
-
49
- expect(breadcrumb).to_not receive(:ignore!)
50
- expect(breadcrumb).to receive(:name=).with("123456789012345678901234567890")
51
- expected_string = "Breadcrumb name trimmed to length 30. Original name: #{name}"
52
- expect(config).to receive(:debug).with(expected_string)
53
-
54
- validator.validate(breadcrumb)
55
- # Check the original message has not been modified
56
- expect(name).to eq("1234567890123456789012345678901234567890")
57
- end
58
-
59
34
  describe "tests meta_data types" do
60
35
  it "accepts Strings, Numerics, Booleans, & nil" do
61
36
  config = instance_double(Bugsnag::Configuration)
@@ -198,4 +173,4 @@ RSpec.describe Bugsnag::Breadcrumbs::Validator do
198
173
  end
199
174
  end
200
175
  end
201
- end
176
+ end
@@ -268,7 +268,7 @@ describe Bugsnag do
268
268
  )
269
269
  expect(breadcrumbs.to_a.size).to eq(1)
270
270
  expect(breadcrumbs.first.to_h).to match({
271
- :name => "123123123123123123123123123123",
271
+ :name => "123123123123123123123123123123456456456456456456456456456456",
272
272
  :type => Bugsnag::Breadcrumbs::MANUAL_BREADCRUMB_TYPE,
273
273
  :metaData => {
274
274
  :a => 1
@@ -311,7 +311,7 @@ describe Bugsnag do
311
311
  Bugsnag.leave_breadcrumb("TestName")
312
312
  expect(breadcrumbs.to_a.size).to eq(1)
313
313
  expect(breadcrumbs.first.to_h).to match({
314
- :name => "123123123123123123123123123123",
314
+ :name => "123123123123123123123123123123456456456456456",
315
315
  :type => Bugsnag::Breadcrumbs::MANUAL_BREADCRUMB_TYPE,
316
316
  :metaData => {
317
317
  :int => 1
@@ -3,15 +3,87 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  describe Bugsnag::Cleaner do
6
- subject { described_class.new(nil) }
6
+ subject { Bugsnag::Cleaner.new(Bugsnag::Configuration.new) }
7
7
 
8
8
  describe "#clean_object" do
9
+ is_jruby = defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
10
+
9
11
  it "cleans up recursive hashes" do
10
12
  a = {:a => {}}
11
13
  a[:a][:b] = a
12
14
  expect(subject.clean_object(a)).to eq({:a => {:b => "[RECURSION]"}})
13
15
  end
14
16
 
17
+ it "cleans up hashes when keys infinitely recurse in to_s" do
18
+ skip "JRuby doesn't allow recovery from SystemStackErrors" if is_jruby
19
+
20
+ class RecursiveHashKey
21
+ def to_s
22
+ to_s
23
+ end
24
+ end
25
+
26
+ key = RecursiveHashKey.new
27
+
28
+ a = {}
29
+ a[key] = 1
30
+
31
+ expect(subject.clean_object(a)).to eq({ "[RECURSION]" => "[FILTERED]" })
32
+ end
33
+
34
+ it "cleans up hashes when a nested key infinitely recurse in to_s" do
35
+ skip "JRuby doesn't allow recovery from SystemStackErrors" if is_jruby
36
+
37
+ class RecursiveHashKey
38
+ def to_s
39
+ to_s
40
+ end
41
+ end
42
+
43
+ key = RecursiveHashKey.new
44
+
45
+ a = {}
46
+ a[:b] = {}
47
+ a[:b][key] = 1
48
+
49
+ expected = { :b => { "[RECURSION]" => "[FILTERED]" } }
50
+
51
+ expect(subject.clean_object(a)).to eq(expected)
52
+ end
53
+
54
+ it "cleans up hashes when keys raise in to_s" do
55
+ class RaisingHashKey
56
+ def to_s
57
+ raise "hey!"
58
+ end
59
+ end
60
+
61
+ key = RaisingHashKey.new
62
+
63
+ a = {}
64
+ a[key] = 1
65
+
66
+ expect(subject.clean_object(a)).to eq({ "[RAISED]" => "[FILTERED]" })
67
+ end
68
+
69
+ it "cleans up hashes when nested keys raise in to_s" do
70
+ class RaisingHashKey
71
+ def to_s
72
+ raise "hey!"
73
+ end
74
+ end
75
+
76
+ key = RaisingHashKey.new
77
+
78
+ a = {}
79
+ a[:b] = {}
80
+ a[:b][key] = 1
81
+
82
+ expected = { :b => { "[RAISED]" => "[FILTERED]" } }
83
+
84
+ expect(subject.clean_object(a)).to eq(expected)
85
+ end
86
+
15
87
  it "cleans up recursive arrays" do
16
88
  a = []
17
89
  a << a
@@ -48,6 +120,31 @@ describe Bugsnag::Cleaner do
48
120
  expect(subject.clean_object(a)).to eq('[OBJECT]')
49
121
  end
50
122
 
123
+ it "cleans custom objects when they infinitely recurse" do
124
+ skip "JRuby doesn't allow recovery from SystemStackErrors" if is_jruby
125
+
126
+ class RecursiveObject
127
+ def to_s
128
+ to_s
129
+ end
130
+ end
131
+
132
+ object = RecursiveObject.new
133
+
134
+ expect(subject.clean_object(object)).to eq("[RECURSION]")
135
+ end
136
+
137
+ it "cleans custom objects to show the id of the object if object responds to id method" do
138
+ class MacaronWithId
139
+ def id
140
+ 10
141
+ end
142
+ end
143
+
144
+ a = MacaronWithId.new
145
+ expect(subject.clean_object(a)).to eq("[OBJECT]: [Class]: #{a.class.name} [ID]: #{a.id}")
146
+ end
147
+
51
148
  it "cleans up binary strings properly" do
52
149
  if RUBY_VERSION > "1.9"
53
150
  obj = "Andr\xc7\xff"
@@ -70,23 +167,113 @@ describe Bugsnag::Cleaner do
70
167
  end
71
168
 
72
169
  it "filters by string inclusion" do
73
- expect(described_class.new(['f']).clean_object({ :foo => 'bar' })).to eq({ :foo => '[FILTERED]' })
74
- expect(described_class.new(['b']).clean_object({ :foo => 'bar' })).to eq({ :foo => 'bar' })
170
+ object = { events: { metaData: { foo: 'bar' } } }
171
+
172
+ configuration = Bugsnag::Configuration.new
173
+ configuration.meta_data_filters = ['f']
174
+
175
+ cleaner = Bugsnag::Cleaner.new(configuration)
176
+ expect(cleaner.clean_object(object)).to eq({ events: { metaData: { foo: '[FILTERED]' } } })
177
+
178
+ configuration = Bugsnag::Configuration.new
179
+ configuration.meta_data_filters = ['b']
180
+
181
+ cleaner = Bugsnag::Cleaner.new(configuration)
182
+ expect(cleaner.clean_object(object)).to eq({ events: { metaData: { foo: 'bar' } } })
75
183
  end
76
184
 
77
185
  it "filters by regular expression" do
78
- expect(described_class.new([/fb?/]).clean_object({ :foo => 'bar' })).to eq({ :foo => '[FILTERED]' })
79
- expect(described_class.new([/fb+/]).clean_object({ :foo => 'bar' })).to eq({ :foo => 'bar' })
186
+ object = { events: { metaData: { foo: 'bar' } } }
187
+
188
+ configuration = Bugsnag::Configuration.new
189
+ configuration.meta_data_filters = [/fb?/]
190
+
191
+ cleaner = Bugsnag::Cleaner.new(configuration)
192
+ expect(cleaner.clean_object(object)).to eq({ events: { metaData: { foo: '[FILTERED]' } } })
193
+
194
+ configuration = Bugsnag::Configuration.new
195
+ configuration.meta_data_filters = [/fb+/]
196
+
197
+ cleaner = Bugsnag::Cleaner.new(configuration)
198
+ expect(cleaner.clean_object(object)).to eq({ events: { metaData: { foo: 'bar' } } })
80
199
  end
81
200
 
82
201
  it "filters deeply nested keys" do
83
- params = {:foo => {:bar => "baz"}}
84
- expect(described_class.new([/^foo\.bar/]).clean_object(params)).to eq({:foo => {:bar => '[FILTERED]'}})
202
+ configuration = Bugsnag::Configuration.new
203
+ configuration.meta_data_filters = [/^foo\.bar/]
204
+
205
+ cleaner = Bugsnag::Cleaner.new(configuration)
206
+
207
+ params = { events: { metaData: { foo: { bar: 'baz' } } } }
208
+ expect(cleaner.clean_object(params)).to eq({ events: { metaData: { foo: { bar: '[FILTERED]' } } } })
85
209
  end
86
210
 
87
211
  it "filters deeply nested request parameters" do
88
- params = {:request => {:params => {:foo => {:bar => "baz"}}}}
89
- expect(described_class.new([/^foo\.bar/]).clean_object(params)).to eq({:request => {:params => {:foo => {:bar => '[FILTERED]'}}}})
212
+ configuration = Bugsnag::Configuration.new
213
+ configuration.meta_data_filters = [/^foo\.bar/]
214
+
215
+ cleaner = Bugsnag::Cleaner.new(configuration)
216
+
217
+ params = { events: { metaData: { request: { params: { foo: { bar: 'baz' } } } } } }
218
+ expect(cleaner.clean_object(params)).to eq({ events: { metaData: { request: { params: { foo: { bar: '[FILTERED]' } } } } } })
219
+ end
220
+
221
+ it "doesn't filter by string inclusion when the scope is not in 'scopes_to_filter'" do
222
+ object = { foo: 'bar' }
223
+
224
+ configuration = Bugsnag::Configuration.new
225
+ configuration.meta_data_filters = ['f']
226
+
227
+ cleaner = Bugsnag::Cleaner.new(configuration)
228
+
229
+ expect(cleaner.clean_object(object)).to eq({ foo: 'bar' })
230
+
231
+ configuration = Bugsnag::Configuration.new
232
+ configuration.meta_data_filters = ['b']
233
+
234
+ cleaner = Bugsnag::Cleaner.new(configuration)
235
+
236
+ expect(cleaner.clean_object(object)).to eq({ foo: 'bar' })
237
+ end
238
+
239
+ it "doesn't filter by regular expression when the scope is not in 'scopes_to_filter'" do
240
+ object = { foo: 'bar' }
241
+
242
+ configuration = Bugsnag::Configuration.new
243
+ configuration.meta_data_filters = [/fb?/]
244
+
245
+ cleaner = Bugsnag::Cleaner.new(configuration)
246
+
247
+ expect(cleaner.clean_object(object)).to eq({ foo: 'bar' })
248
+
249
+ configuration = Bugsnag::Configuration.new
250
+ configuration.meta_data_filters = [/fb+/]
251
+
252
+ cleaner = Bugsnag::Cleaner.new(configuration)
253
+
254
+ expect(cleaner.clean_object(object)).to eq({ foo: 'bar' })
255
+ end
256
+
257
+ it "doesn't filter deeply nested keys when the scope is not in 'scopes_to_filter'" do
258
+ params = { foo: { bar: 'baz' } }
259
+
260
+ configuration = Bugsnag::Configuration.new
261
+ configuration.meta_data_filters = [/^foo\.bar/]
262
+
263
+ cleaner = Bugsnag::Cleaner.new(configuration)
264
+
265
+ expect(cleaner.clean_object(params)).to eq({ foo: { bar: 'baz' } })
266
+ end
267
+
268
+ it "doesn't filter deeply nested request parameters when the scope is not in 'scopes_to_filter'" do
269
+ params = { request: { params: { foo: { bar: 'baz' } } } }
270
+
271
+ configuration = Bugsnag::Configuration.new
272
+ configuration.meta_data_filters = [/^foo\.bar/]
273
+
274
+ cleaner = Bugsnag::Cleaner.new(configuration)
275
+
276
+ expect(cleaner.clean_object(params)).to eq({ request: { params: { foo: { bar: 'baz' } } } })
90
277
  end
91
278
 
92
279
  it "filters objects which can't be stringified" do
@@ -101,7 +288,12 @@ describe Bugsnag::Cleaner do
101
288
 
102
289
  describe "#clean_url" do
103
290
  let(:filters) { [] }
104
- subject { described_class.new(filters).clean_url(url) }
291
+
292
+ subject do
293
+ configuration = Bugsnag::Configuration.new
294
+ configuration.meta_data_filters = filters
295
+ described_class.new(configuration).clean_url(url)
296
+ end
105
297
 
106
298
  context "with no filters configured" do
107
299
  let(:url) { "/dir/page?param1=value1&param2=value2" }