bugsnag 6.13.1 → 6.14.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +55 -0
- data/.rubocop_todo.yml +530 -160
- data/CHANGELOG.md +25 -0
- data/Gemfile +3 -1
- data/TESTING.md +3 -3
- data/VERSION +1 -1
- data/dockerfiles/Dockerfile.ruby-maze-runner +1 -1
- data/features/fixtures/rails3/app/config/initializers/bugsnag.rb +2 -1
- data/features/fixtures/rails4/app/config/initializers/bugsnag.rb +1 -0
- data/features/fixtures/rails5/app/config/initializers/bugsnag.rb +1 -0
- data/features/fixtures/rails6/app/config/initializers/bugsnag.rb +1 -0
- data/features/rails_features/meta_data_filters.feature +4 -2
- data/lib/bugsnag.rb +74 -21
- data/lib/bugsnag/breadcrumbs/breadcrumbs.rb +0 -2
- data/lib/bugsnag/breadcrumbs/validator.rb +0 -6
- data/lib/bugsnag/cleaner.rb +109 -56
- data/lib/bugsnag/configuration.rb +20 -2
- data/lib/bugsnag/helpers.rb +2 -4
- data/lib/bugsnag/middleware/discard_error_class.rb +30 -0
- data/lib/bugsnag/middleware/ignore_error_class.rb +2 -0
- data/lib/bugsnag/middleware/rack_request.rb +2 -4
- data/lib/bugsnag/report.rb +3 -13
- data/lib/bugsnag/stacktrace.rb +5 -6
- data/spec/breadcrumbs/breadcrumb_spec.rb +1 -1
- data/spec/breadcrumbs/validator_spec.rb +1 -26
- data/spec/bugsnag_spec.rb +2 -2
- data/spec/cleaner_spec.rb +116 -10
- data/spec/configuration_spec.rb +5 -1
- data/spec/helper_spec.rb +0 -86
- data/spec/integrations/rack_spec.rb +8 -6
- data/spec/report_spec.rb +324 -26
- data/spec/spec_helper.rb +6 -1
- data/spec/stacktrace_spec.rb +141 -73
- metadata +3 -2
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,31 @@
|
|
1
1
|
Changelog
|
2
2
|
=========
|
3
3
|
|
4
|
+
## 6.14.0 (20 July 2020)
|
5
|
+
|
6
|
+
### Enhancements
|
7
|
+
|
8
|
+
* Add configurable `discard_classes` option to allow filtering errors using either a `String` or `Regexp` matched against the error's class name
|
9
|
+
| [#597](https://github.com/bugsnag/bugsnag-ruby/pull/597)
|
10
|
+
|
11
|
+
* The Breadcrumb name limit of 30 characters has been removed
|
12
|
+
| [#600](https://github.com/bugsnag/bugsnag-ruby/pull/600)
|
13
|
+
|
14
|
+
* Improve performance of payload cleaning
|
15
|
+
| [#601](https://github.com/bugsnag/bugsnag-ruby/pull/601)
|
16
|
+
|
17
|
+
* Improve performance when processing stacktraces
|
18
|
+
| [#602](https://github.com/bugsnag/bugsnag-ruby/pull/602)
|
19
|
+
| [#603](https://github.com/bugsnag/bugsnag-ruby/pull/603)
|
20
|
+
|
21
|
+
* If a custom object responds to `id` method, show the id and class in error reports
|
22
|
+
| [#531](https://github.com/bugsnag/bugsnag-ruby/pull/531)
|
23
|
+
| [manojmj92](https://github.com/manojmj92)
|
24
|
+
|
25
|
+
### Deprecated
|
26
|
+
|
27
|
+
* The `ignore_classes` configuration option has been deprecated in favour of `discard_classes`. `ignore_classes` will be removed in the next major release
|
28
|
+
|
4
29
|
## 6.13.1 (11 May 2020)
|
5
30
|
|
6
31
|
### Fixes
|
data/Gemfile
CHANGED
@@ -32,13 +32,15 @@ group :coverage, optional: true do
|
|
32
32
|
end
|
33
33
|
|
34
34
|
group :rubocop, optional: true do
|
35
|
-
gem 'rubocop', '~> 0.
|
35
|
+
gem 'rubocop', '~> 0.83'
|
36
36
|
end
|
37
37
|
|
38
38
|
group :sidekiq, optional: true do
|
39
39
|
gem 'sidekiq', '~> 5.2.7'
|
40
40
|
# redis 4.1.2 dropped support for Ruby 2.2
|
41
41
|
gem 'redis', ruby_version < Gem::Version.new('2.3.0') ? '4.1.1' : '>= 4.1.2'
|
42
|
+
# rack 2.2.0 dropped support for Ruby 2.2
|
43
|
+
gem 'rack', ruby_version < Gem::Version.new('2.3.0') ? '< 2.2.0' : '~> 2.2'
|
42
44
|
end
|
43
45
|
|
44
46
|
group :doc, optional: true do
|
data/TESTING.md
CHANGED
@@ -42,7 +42,7 @@ aws configure --profile=opensource
|
|
42
42
|
Subsequently you'll need to run the following commmand to authenticate with the registry:
|
43
43
|
|
44
44
|
```
|
45
|
-
|
45
|
+
aws ecr get-login-password --profile=opensource | docker login --username AWS --password-stdin 855461928731.dkr.ecr.us-west-1.amazonaws.com
|
46
46
|
```
|
47
47
|
|
48
48
|
__Your session will periodically expire__, so you'll need to run this command to re-authenticate when that happens.
|
@@ -64,7 +64,7 @@ Configure the tests to be run in the following way:
|
|
64
64
|
When running the end-to-end tests, you'll want to restrict the feature files run to the specific test features for the platform. This is done using the Cucumber CLI syntax at the end of the `docker-compose run ruby-maze-runner` command, i.e:
|
65
65
|
|
66
66
|
```
|
67
|
-
RUBY_TEST_VERSION=2.6 RAILS_VERSION=6 docker-compose run ruby-maze-runner features/rails_features --tags "@rails6"
|
67
|
+
RUBY_TEST_VERSION=2.6 RAILS_VERSION=6 docker-compose run --use-aliases ruby-maze-runner features/rails_features --tags "@rails6"
|
68
68
|
```
|
69
69
|
|
70
70
|
- Plain ruby tests should target `features/plain_features`
|
@@ -75,7 +75,7 @@ RUBY_TEST_VERSION=2.6 RAILS_VERSION=6 docker-compose run ruby-maze-runner featur
|
|
75
75
|
In order to target specific features the exact `.feature` file can be specified, i.e:
|
76
76
|
|
77
77
|
```
|
78
|
-
RUBY_TEST_VERSION=2.6 RAILS_VERSION=6 docker-compose run ruby-maze-runner features/rails_features/app_version.feature --tags "@rails6"
|
78
|
+
RUBY_TEST_VERSION=2.6 RAILS_VERSION=6 docker-compose run --use-aliases ruby-maze-runner features/rails_features/app_version.feature --tags "@rails6"
|
79
79
|
```
|
80
80
|
|
81
81
|
In order to avoid running flakey or unfinished tests, the tag `"not @wip"` can be added to the tags option. This is recommended for all CI runs. If a tag is already specified, this should be added using the `and` keyword, e.g. `--tags "@rails6 and not @wip"`
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
6.
|
1
|
+
6.14.0
|
@@ -20,7 +20,7 @@ COPY spec ./spec
|
|
20
20
|
RUN gem build bugsnag.gemspec
|
21
21
|
|
22
22
|
# The maze-runner node tests
|
23
|
-
FROM 855461928731.dkr.ecr.us-west-1.amazonaws.com/maze-runner:
|
23
|
+
FROM 855461928731.dkr.ecr.us-west-1.amazonaws.com/maze-runner:latest-cli as ruby-maze-runner
|
24
24
|
WORKDIR /app/
|
25
25
|
COPY features ./features
|
26
26
|
COPY --from=ruby-package-builder /app/bugsnag-*.gem bugsnag.gem
|
@@ -11,10 +11,11 @@ Bugsnag.configure do |config|
|
|
11
11
|
config.release_stage = ENV["BUGSNAG_RELEASE_STAGE"] if ENV.include? "BUGSNAG_RELEASE_STAGE"
|
12
12
|
config.send_code = ENV["BUGSNAG_SEND_CODE"] != "false"
|
13
13
|
config.send_environment = ENV["BUGSNAG_SEND_ENVIRONMENT"] == "true"
|
14
|
+
config.meta_data_filters << 'filtered_parameter'
|
14
15
|
|
15
16
|
if ENV["SQL_ONLY_BREADCRUMBS"] == "true"
|
16
17
|
config.before_breadcrumb_callbacks << Proc.new do |breadcrumb|
|
17
18
|
breadcrumb.ignore! unless breadcrumb.meta_data[:event_name] == "sql.active_record" && breadcrumb.meta_data[:name] == "User Load"
|
18
19
|
end
|
19
20
|
end
|
20
|
-
end
|
21
|
+
end
|
@@ -11,6 +11,7 @@ Bugsnag.configure do |config|
|
|
11
11
|
config.release_stage = ENV["BUGSNAG_RELEASE_STAGE"] if ENV.include? "BUGSNAG_RELEASE_STAGE"
|
12
12
|
config.send_code = ENV["BUGSNAG_SEND_CODE"] != "false"
|
13
13
|
config.send_environment = ENV["BUGSNAG_SEND_ENVIRONMENT"] == "true"
|
14
|
+
config.meta_data_filters << 'filtered_parameter'
|
14
15
|
|
15
16
|
if ENV["SQL_ONLY_BREADCRUMBS"] == "true"
|
16
17
|
config.before_breadcrumb_callbacks << Proc.new do |breadcrumb|
|
@@ -11,6 +11,7 @@ Bugsnag.configure do |config|
|
|
11
11
|
config.release_stage = ENV["BUGSNAG_RELEASE_STAGE"] if ENV.include? "BUGSNAG_RELEASE_STAGE"
|
12
12
|
config.send_code = ENV["BUGSNAG_SEND_CODE"] != "false"
|
13
13
|
config.send_environment = ENV["BUGSNAG_SEND_ENVIRONMENT"] == "true"
|
14
|
+
config.meta_data_filters << 'filtered_parameter'
|
14
15
|
|
15
16
|
if ENV["SQL_ONLY_BREADCRUMBS"] == "true"
|
16
17
|
config.before_breadcrumb_callbacks << Proc.new do |breadcrumb|
|
@@ -11,6 +11,7 @@ Bugsnag.configure do |config|
|
|
11
11
|
config.release_stage = ENV["BUGSNAG_RELEASE_STAGE"] if ENV.include? "BUGSNAG_RELEASE_STAGE"
|
12
12
|
config.send_code = ENV["BUGSNAG_SEND_CODE"] != "false"
|
13
13
|
config.send_environment = ENV["BUGSNAG_SEND_ENVIRONMENT"] == "true"
|
14
|
+
config.meta_data_filters << 'filtered_parameter'
|
14
15
|
|
15
16
|
if ENV["SQL_ONLY_BREADCRUMBS"] == "true"
|
16
17
|
config.before_breadcrumb_callbacks << Proc.new do |breadcrumb|
|
@@ -3,12 +3,14 @@ Feature: Metadata filters
|
|
3
3
|
@rails3 @rails4 @rails5 @rails6
|
4
4
|
Scenario: Meta_data_filters should include Rails.configuration.filter_parameters
|
5
5
|
Given I start the rails service
|
6
|
-
When I navigate to the route "/metadata_filters/filter" on the rails app
|
6
|
+
When I navigate to the route "/metadata_filters/filter?filtered_parameter=foo&other_parameter=bar" on the rails app
|
7
7
|
And I wait to receive a request
|
8
8
|
Then the request is valid for the error reporting API version "4.0" for the "Ruby Bugsnag Notifier"
|
9
9
|
And the event "unhandled" is false
|
10
10
|
And the exception "errorClass" equals "RuntimeError"
|
11
11
|
And the exception "message" starts with "handled string"
|
12
12
|
And the event "app.type" equals "rails"
|
13
|
-
And the event "metaData.request.url" ends with "/metadata_filters/filter"
|
13
|
+
And the event "metaData.request.url" ends with "/metadata_filters/filter?filtered_parameter=[FILTERED]&other_parameter=bar"
|
14
14
|
And the event "metaData.my_specific_filter" equals "[FILTERED]"
|
15
|
+
And the event "metaData.request.params.filtered_parameter" equals "[FILTERED]"
|
16
|
+
And the event "metaData.request.params.other_parameter" equals "bar"
|
data/lib/bugsnag.rb
CHANGED
@@ -33,6 +33,7 @@ require "bugsnag/breadcrumbs/validator"
|
|
33
33
|
require "bugsnag/breadcrumbs/breadcrumb"
|
34
34
|
require "bugsnag/breadcrumbs/breadcrumbs"
|
35
35
|
|
36
|
+
# rubocop:todo Metrics/ModuleLength
|
36
37
|
module Bugsnag
|
37
38
|
LOCK = Mutex.new
|
38
39
|
INTEGRATIONS = [:resque, :sidekiq, :mailman, :delayed_job, :shoryuken, :que, :mongo]
|
@@ -63,7 +64,7 @@ module Bugsnag
|
|
63
64
|
auto_notify = false
|
64
65
|
end
|
65
66
|
|
66
|
-
return unless
|
67
|
+
return unless should_deliver_notification?(exception, auto_notify)
|
67
68
|
|
68
69
|
exception = NIL_EXCEPTION_DESCRIPTION if exception.nil?
|
69
70
|
|
@@ -71,6 +72,7 @@ module Bugsnag
|
|
71
72
|
|
72
73
|
# If this is an auto_notify we yield the block before the any middleware is run
|
73
74
|
yield(report) if block_given? && auto_notify
|
75
|
+
|
74
76
|
if report.ignore?
|
75
77
|
configuration.debug("Not notifying #{report.exceptions.last[:errorClass]} due to ignore being signified in auto_notify block")
|
76
78
|
return
|
@@ -97,6 +99,7 @@ module Bugsnag
|
|
97
99
|
# If this is not an auto_notify then the block was provided by the user. This should be the last
|
98
100
|
# block that is run as it is the users "most specific" block.
|
99
101
|
yield(report) if block_given? && !auto_notify
|
102
|
+
|
100
103
|
if report.ignore?
|
101
104
|
configuration.debug("Not notifying #{report.exceptions.last[:errorClass]} due to ignore being signified in user provided block")
|
102
105
|
return
|
@@ -111,13 +114,7 @@ module Bugsnag
|
|
111
114
|
report.severity_reason = initial_reason
|
112
115
|
end
|
113
116
|
|
114
|
-
|
115
|
-
configuration.info("Notifying #{configuration.notify_endpoint} of #{report.exceptions.last[:errorClass]}")
|
116
|
-
options = {:headers => report.headers}
|
117
|
-
payload = ::JSON.dump(Bugsnag::Helpers.trim_if_needed(report.as_json))
|
118
|
-
Bugsnag::Delivery[configuration.delivery_method].deliver(configuration.notify_endpoint, payload, configuration, options)
|
119
|
-
report_summary = report.summary
|
120
|
-
leave_breadcrumb(report_summary[:error_class], report_summary, Bugsnag::Breadcrumbs::ERROR_BREADCRUMB_TYPE, :auto)
|
117
|
+
deliver_notification(report)
|
121
118
|
end
|
122
119
|
end
|
123
120
|
|
@@ -147,6 +144,7 @@ module Bugsnag
|
|
147
144
|
# Configuration getters
|
148
145
|
##
|
149
146
|
# Returns the client's Configuration object, or creates one if not yet created.
|
147
|
+
# @return [Configuration]
|
150
148
|
def configuration
|
151
149
|
@configuration = nil unless defined?(@configuration)
|
152
150
|
@configuration || LOCK.synchronize { @configuration ||= Bugsnag::Configuration.new }
|
@@ -211,27 +209,40 @@ module Bugsnag
|
|
211
209
|
validator.validate(breadcrumb)
|
212
210
|
|
213
211
|
# Skip if it's already invalid
|
214
|
-
|
215
|
-
# Run callbacks
|
216
|
-
configuration.before_breadcrumb_callbacks.each do |c|
|
217
|
-
c.arity > 0 ? c.call(breadcrumb) : c.call
|
218
|
-
break if breadcrumb.ignore?
|
219
|
-
end
|
212
|
+
return if breadcrumb.ignore?
|
220
213
|
|
221
|
-
|
222
|
-
|
214
|
+
# Run callbacks
|
215
|
+
configuration.before_breadcrumb_callbacks.each do |c|
|
216
|
+
c.arity > 0 ? c.call(breadcrumb) : c.call
|
217
|
+
break if breadcrumb.ignore?
|
218
|
+
end
|
219
|
+
|
220
|
+
# Return early if ignored
|
221
|
+
return if breadcrumb.ignore?
|
223
222
|
|
224
|
-
|
225
|
-
|
223
|
+
# Validate again in case of callback alteration
|
224
|
+
validator.validate(breadcrumb)
|
226
225
|
|
227
|
-
|
228
|
-
|
226
|
+
# Add to breadcrumbs buffer if still valid
|
227
|
+
configuration.breadcrumbs << breadcrumb unless breadcrumb.ignore?
|
228
|
+
end
|
229
|
+
|
230
|
+
##
|
231
|
+
# Returns the client's Cleaner object, or creates one if not yet created.
|
232
|
+
#
|
233
|
+
# @api private
|
234
|
+
#
|
235
|
+
# @return [Cleaner]
|
236
|
+
def cleaner
|
237
|
+
@cleaner = nil unless defined?(@cleaner)
|
238
|
+
@cleaner || LOCK.synchronize do
|
239
|
+
@cleaner ||= Bugsnag::Cleaner.new(configuration)
|
229
240
|
end
|
230
241
|
end
|
231
242
|
|
232
243
|
private
|
233
244
|
|
234
|
-
def
|
245
|
+
def should_deliver_notification?(exception, auto_notify)
|
235
246
|
reason = abort_reason(exception, auto_notify)
|
236
247
|
configuration.debug(reason) unless reason.nil?
|
237
248
|
reason.nil?
|
@@ -249,6 +260,32 @@ module Bugsnag
|
|
249
260
|
end
|
250
261
|
end
|
251
262
|
|
263
|
+
##
|
264
|
+
# Deliver the notification to Bugsnag
|
265
|
+
#
|
266
|
+
# @param report [Report]
|
267
|
+
# @return void
|
268
|
+
def deliver_notification(report)
|
269
|
+
configuration.info("Notifying #{configuration.notify_endpoint} of #{report.exceptions.last[:errorClass]}")
|
270
|
+
|
271
|
+
payload = report_to_json(report)
|
272
|
+
options = {:headers => report.headers}
|
273
|
+
|
274
|
+
Bugsnag::Delivery[configuration.delivery_method].deliver(
|
275
|
+
configuration.notify_endpoint,
|
276
|
+
payload,
|
277
|
+
configuration,
|
278
|
+
options
|
279
|
+
)
|
280
|
+
|
281
|
+
leave_breadcrumb(
|
282
|
+
report.summary[:error_class],
|
283
|
+
report.summary,
|
284
|
+
Bugsnag::Breadcrumbs::ERROR_BREADCRUMB_TYPE,
|
285
|
+
:auto
|
286
|
+
)
|
287
|
+
end
|
288
|
+
|
252
289
|
# Check if the API key is valid and warn (once) if it is not
|
253
290
|
def check_key_valid
|
254
291
|
@key_warning = false unless defined?(@key_warning)
|
@@ -273,7 +310,23 @@ module Bugsnag
|
|
273
310
|
raise ArgumentError, "The session endpoint cannot be modified without the notify endpoint"
|
274
311
|
end
|
275
312
|
end
|
313
|
+
|
314
|
+
##
|
315
|
+
# Convert the Report object to JSON
|
316
|
+
#
|
317
|
+
# We ensure the report is safe to send by removing recursion, fixing
|
318
|
+
# encoding errors and redacting metadata according to "meta_data_filters"
|
319
|
+
#
|
320
|
+
# @param report [Report]
|
321
|
+
# @return string
|
322
|
+
def report_to_json(report)
|
323
|
+
cleaned = cleaner.clean_object(report.as_json)
|
324
|
+
trimmed = Bugsnag::Helpers.trim_if_needed(cleaned)
|
325
|
+
|
326
|
+
::JSON.dump(trimmed)
|
327
|
+
end
|
276
328
|
end
|
277
329
|
end
|
330
|
+
# rubocop:enable Metrics/ModuleLength
|
278
331
|
|
279
332
|
Bugsnag.load_integrations unless ENV["BUGSNAG_DISABLE_AUTOCONFIGURE"]
|
@@ -15,12 +15,6 @@ module Bugsnag::Breadcrumbs
|
|
15
15
|
#
|
16
16
|
# @param breadcrumb [Bugsnag::Breadcrumbs::Breadcrumb] the breadcrumb to be validated
|
17
17
|
def validate(breadcrumb)
|
18
|
-
# Check name length
|
19
|
-
if breadcrumb.name.size > Bugsnag::Breadcrumbs::MAX_NAME_LENGTH
|
20
|
-
@configuration.debug("Breadcrumb name trimmed to length #{Bugsnag::Breadcrumbs::MAX_NAME_LENGTH}. Original name: #{breadcrumb.name}")
|
21
|
-
breadcrumb.name = breadcrumb.name.slice(0...Bugsnag::Breadcrumbs::MAX_NAME_LENGTH)
|
22
|
-
end
|
23
|
-
|
24
18
|
# Check meta_data hash doesn't contain complex values
|
25
19
|
breadcrumb.meta_data = breadcrumb.meta_data.select do |k, v|
|
26
20
|
if valid_meta_data_type?(v)
|
data/lib/bugsnag/cleaner.rb
CHANGED
@@ -1,19 +1,76 @@
|
|
1
1
|
require 'uri'
|
2
2
|
|
3
3
|
module Bugsnag
|
4
|
+
# @api private
|
4
5
|
class Cleaner
|
5
6
|
FILTERED = '[FILTERED]'.freeze
|
6
7
|
RECURSION = '[RECURSION]'.freeze
|
7
8
|
OBJECT = '[OBJECT]'.freeze
|
8
9
|
RAISED = '[RAISED]'.freeze
|
10
|
+
OBJECT_WITH_ID_AND_CLASS = '[OBJECT]: [Class]: %<class_name>s [ID]: %<id>d'.freeze
|
9
11
|
|
10
|
-
|
11
|
-
|
12
|
-
|
12
|
+
##
|
13
|
+
# @param configuration [Configuration]
|
14
|
+
def initialize(configuration)
|
15
|
+
@configuration = configuration
|
13
16
|
end
|
14
17
|
|
15
|
-
def clean_object(
|
16
|
-
|
18
|
+
def clean_object(object)
|
19
|
+
@deep_filters = deep_filters?
|
20
|
+
|
21
|
+
traverse_object(object, {}, nil)
|
22
|
+
end
|
23
|
+
|
24
|
+
##
|
25
|
+
# @param url [String]
|
26
|
+
# @return [String]
|
27
|
+
def clean_url(url)
|
28
|
+
return url if @configuration.meta_data_filters.empty?
|
29
|
+
|
30
|
+
uri = URI(url)
|
31
|
+
return url unless uri.query
|
32
|
+
|
33
|
+
query_params = uri.query.split('&').map { |pair| pair.split('=') }
|
34
|
+
query_params.map! do |key, val|
|
35
|
+
if filters_match?(key)
|
36
|
+
"#{key}=#{FILTERED}"
|
37
|
+
else
|
38
|
+
"#{key}=#{val}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
uri.query = query_params.join('&')
|
43
|
+
uri.to_s
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
##
|
49
|
+
# This method calculates whether we need to filter deeply or not; i.e. whether
|
50
|
+
# we should match both with and without 'request.params'
|
51
|
+
#
|
52
|
+
# This is cached on the instance variable '@deep_filters' for performance
|
53
|
+
# reasons
|
54
|
+
#
|
55
|
+
# @return [Boolean]
|
56
|
+
def deep_filters?
|
57
|
+
@configuration.meta_data_filters.any? do |filter|
|
58
|
+
filter.is_a?(Regexp) && filter.to_s.include?("\\.".freeze)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def clean_string(str)
|
63
|
+
if defined?(str.encoding) && defined?(Encoding::UTF_8)
|
64
|
+
if str.encoding == Encoding::UTF_8
|
65
|
+
str.valid_encoding? ? str : str.encode('utf-16', invalid: :replace, undef: :replace).encode('utf-8')
|
66
|
+
else
|
67
|
+
str.encode('utf-8', invalid: :replace, undef: :replace)
|
68
|
+
end
|
69
|
+
elsif defined?(Iconv)
|
70
|
+
Iconv.conv('UTF-8//IGNORE', 'UTF-8', str) || str
|
71
|
+
else
|
72
|
+
str
|
73
|
+
end
|
17
74
|
end
|
18
75
|
|
19
76
|
def traverse_object(obj, seen, scope)
|
@@ -28,12 +85,14 @@ module Bugsnag
|
|
28
85
|
value = case obj
|
29
86
|
when Hash
|
30
87
|
clean_hash = {}
|
31
|
-
obj.each do |k,v|
|
88
|
+
obj.each do |k, v|
|
32
89
|
begin
|
33
|
-
|
90
|
+
current_scope = [scope, k].compact.join('.')
|
91
|
+
|
92
|
+
if filters_match_deeply?(k, current_scope)
|
34
93
|
clean_hash[k] = FILTERED
|
35
94
|
else
|
36
|
-
clean_hash[k] = traverse_object(v, seen,
|
95
|
+
clean_hash[k] = traverse_object(v, seen, current_scope)
|
37
96
|
end
|
38
97
|
# If we get an error here, we assume the key needs to be filtered
|
39
98
|
# to avoid leaking things we shouldn't. We also remove the key itself
|
@@ -63,7 +122,12 @@ module Bugsnag
|
|
63
122
|
|
64
123
|
# avoid leaking potentially sensitive data from objects' #inspect output
|
65
124
|
if str =~ /#<.*>/
|
66
|
-
|
125
|
+
# Use id of the object if available
|
126
|
+
if obj.respond_to?(:id)
|
127
|
+
format(OBJECT_WITH_ID_AND_CLASS, class_name: obj.class, id: obj.id)
|
128
|
+
else
|
129
|
+
OBJECT
|
130
|
+
end
|
67
131
|
else
|
68
132
|
clean_string(str)
|
69
133
|
end
|
@@ -73,67 +137,56 @@ module Bugsnag
|
|
73
137
|
value
|
74
138
|
end
|
75
139
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
140
|
+
##
|
141
|
+
# @param key [String, #to_s]
|
142
|
+
# @return [Boolean]
|
143
|
+
def filters_match?(key)
|
144
|
+
str = key.to_s
|
145
|
+
|
146
|
+
@configuration.meta_data_filters.any? do |filter|
|
147
|
+
case filter
|
148
|
+
when Regexp
|
149
|
+
str.match(filter)
|
80
150
|
else
|
81
|
-
str.
|
151
|
+
str.include?(filter.to_s)
|
82
152
|
end
|
83
|
-
elsif defined?(Iconv)
|
84
|
-
Iconv.conv('UTF-8//IGNORE', 'UTF-8', str) || str
|
85
|
-
else
|
86
|
-
str
|
87
153
|
end
|
88
154
|
end
|
89
155
|
|
90
|
-
|
91
|
-
|
92
|
-
|
156
|
+
##
|
157
|
+
# If someone has a Rails filter like /^stuff\.secret/, it won't match
|
158
|
+
# "request.params.stuff.secret", so we try it both with and without the
|
159
|
+
# "request.params." bit.
|
160
|
+
#
|
161
|
+
# @param key [String, #to_s]
|
162
|
+
# @param scope [String]
|
163
|
+
# @return [Boolean]
|
164
|
+
def filters_match_deeply?(key, scope)
|
165
|
+
return false unless scope_should_be_filtered?(scope)
|
93
166
|
|
94
|
-
|
95
|
-
return
|
167
|
+
return true if filters_match?(key)
|
168
|
+
return false unless @deep_filters
|
96
169
|
|
97
|
-
|
98
|
-
return url unless uri.query
|
170
|
+
return true if filters_match?(scope)
|
99
171
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
"#{key}=#{FILTERED}"
|
172
|
+
@configuration.scopes_to_filter.any? do |scope_to_filter|
|
173
|
+
if scope.start_with?("#{scope_to_filter}.request.params.")
|
174
|
+
filters_match?(scope.sub("#{scope_to_filter}.request.params.", ''))
|
104
175
|
else
|
105
|
-
"#{
|
176
|
+
filters_match?(scope.sub("#{scope_to_filter}.", ''))
|
106
177
|
end
|
107
178
|
end
|
108
|
-
|
109
|
-
uri.query = query_params.join('&')
|
110
|
-
uri.to_s
|
111
179
|
end
|
112
180
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
str.match(f)
|
122
|
-
else
|
123
|
-
str.include?(f.to_s)
|
124
|
-
end
|
181
|
+
##
|
182
|
+
# Should the given scope be filtered?
|
183
|
+
#
|
184
|
+
# @param scope [String]
|
185
|
+
# @return [Boolean]
|
186
|
+
def scope_should_be_filtered?(scope)
|
187
|
+
@configuration.scopes_to_filter.any? do |scope_to_filter|
|
188
|
+
scope.start_with?("#{scope_to_filter}.")
|
125
189
|
end
|
126
190
|
end
|
127
|
-
|
128
|
-
# If someone has a Rails filter like /^stuff\.secret/, it won't match "request.params.stuff.secret",
|
129
|
-
# so we try it both with and without the "request.params." bit.
|
130
|
-
def filters_match_deeply?(key, scope)
|
131
|
-
return true if filters_match?(key)
|
132
|
-
return false unless @deep_filters
|
133
|
-
|
134
|
-
long = [scope, key].compact.join('.')
|
135
|
-
short = long.sub(/^request\.params\./, '')
|
136
|
-
filters_match?(long) || filters_match?(short)
|
137
|
-
end
|
138
191
|
end
|
139
192
|
end
|