bugsnag 4.0.0 → 4.0.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0ebe81525d5bc908bded68c8fe66f2148c827bf2
4
- data.tar.gz: 5094f9cbca6d554b8ab8599dd8cf751824f39564
3
+ metadata.gz: 3feeeeef834abcd2676f54ed08468f35d2e61cec
4
+ data.tar.gz: fa7ff7ef9f2e1619d3d00142f6e7eb1c241c514e
5
5
  SHA512:
6
- metadata.gz: 7893ddc4139330cddecddee79e8d25306385a2aa1d9d1e73d4d715af856aa03c9f534614b2747291136910df04fdab64416e15ef476bdec6b8b8b04363fc23e1
7
- data.tar.gz: a48b1f8f0745b50ec99322f98d5fb7dd37f3df0b3b3fa46fb2e2894043a6afd484c1710ed346deb8f61461be9de1a293e4f67e83f4260fb3e30245f8799f36c1
6
+ metadata.gz: 08264576a36649e2850368e7f6d6c2190d9fe44d4489c963c464f7abcb4201a45a72b9d773a1d26eff2c98be41a0b18e953e295f06156ffe6e56de28a2601625
7
+ data.tar.gz: cd2424431cae73dea1e41f0b570e241834a063be6cf9c4a59964031467c77ed9089ab8ee312de9a11570a55de019e4bd109eae6e544f2355b1b09fa761948ea5
@@ -1,6 +1,27 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
+ ## 4.0.1 (5 Apr 2016)
5
+
6
+ ### Fixes
7
+
8
+ * Fix Sidekiq app type being overwritten by Rails
9
+ | [Luiz Felipe G. Pereira](https://github.com/Draiken)
10
+ | [#286](https://github.com/bugsnag/bugsnag-ruby/pull/286)
11
+
12
+ * Fix report uploads being rejected due to payload size
13
+ | [Ben Ibinson](https://github.com/CodeHex)
14
+ | [Duncan Hewett](https://github.com/duncanhewett)
15
+ | [#288](https://github.com/bugsnag/bugsnag-ruby/pull/288)
16
+ | [#290](https://github.com/bugsnag/bugsnag-ruby/pull/290)
17
+
18
+ * Fix a possible crash when parsing a URL for RackRequest
19
+ | [Max Schubert](https://github.com/perldork)
20
+ | [#289](https://github.com/bugsnag/bugsnag-ruby/pull/289)
21
+
22
+ * Hide partial API key logged when loading Bugsnag
23
+ | [#283](https://github.com/bugsnag/bugsnag-ruby/issues/283)
24
+
4
25
  ## 4.0.0 (9 Mar 2016)
5
26
 
6
27
  This release includes general fixes as well as removing support
data/VERSION CHANGED
@@ -1 +1 @@
1
- 4.0.0
1
+ 4.0.1
@@ -47,7 +47,7 @@ module Bugsnag
47
47
  @logged_ready = false unless defined?(@logged_ready)
48
48
 
49
49
  if configuration.api_key && !@logged_ready
50
- log "Bugsnag exception handler #{VERSION} ready, api_key=#{configuration.api_key}"
50
+ log "Bugsnag exception handler #{VERSION} ready"
51
51
  @logged_ready = true
52
52
  end
53
53
  end
@@ -1,24 +1,21 @@
1
1
  require 'uri'
2
+ require 'set' unless defined?(Set)
3
+ require 'json' unless defined?(JSON)
4
+
2
5
 
3
6
  module Bugsnag
4
7
  module Helpers
5
8
  MAX_STRING_LENGTH = 4096
9
+ MAX_PAYLOAD_LENGTH = 128000
10
+ MAX_ARRAY_LENGTH = 400
6
11
 
7
- def self.reduce_hash_size(hash)
8
- return {} unless hash.is_a?(Hash)
9
- hash.inject({}) do |h, (k,v)|
10
- if v.is_a?(Hash)
11
- h[k] = reduce_hash_size(v)
12
- elsif v.is_a?(Array) || v.is_a?(Set)
13
- h[k] = v.map {|el| reduce_hash_size(el) }
14
- else
15
- val = v.to_s
16
- val = val.slice(0, MAX_STRING_LENGTH) + "[TRUNCATED]" if val.length > MAX_STRING_LENGTH
17
- h[k] = val
18
- end
19
-
20
- h
21
- end
12
+ # Trim the size of value if the serialized JSON value is longer than is
13
+ # accepted by Bugsnag
14
+ def self.trim_if_needed(value)
15
+ return value unless payload_too_long?(value)
16
+ reduced_value = trim_strings_in_value(value)
17
+ return reduced_value unless payload_too_long?(reduced_value)
18
+ truncate_arrays_in_value(reduced_value)
22
19
  end
23
20
 
24
21
  def self.flatten_meta_data(overrides)
@@ -31,5 +28,95 @@ module Bugsnag
31
28
  overrides
32
29
  end
33
30
  end
31
+
32
+ private
33
+
34
+ TRUNCATION_INFO = '[TRUNCATED]'
35
+ RAW_DATA_TYPES = [Numeric, TrueClass, FalseClass]
36
+
37
+ # Shorten array until it fits within the payload size limit when serialized
38
+ def self.truncate_arrays(array)
39
+ return [] unless array.respond_to?(:slice)
40
+ array = array.slice(0, MAX_ARRAY_LENGTH)
41
+ while array.length > 0 and payload_too_long?(array)
42
+ array = array.slice(0, array.length - 1)
43
+ end
44
+ array
45
+ end
46
+
47
+ # Trim all strings to be less than the maximum allowed string length
48
+ def self.trim_strings_in_value(value, seen=[])
49
+ return value if is_json_raw_type?(value)
50
+ case value
51
+ when Hash
52
+ trim_strings_in_hash(value, seen)
53
+ when Array, Set
54
+ trim_strings_in_array(value, seen)
55
+ else
56
+ trim_as_string(value)
57
+ end
58
+ end
59
+
60
+ # Validate that the serialized JSON string value is below maximum payload
61
+ # length
62
+ def self.payload_too_long?(value)
63
+ ::JSON.dump(value).length >= MAX_PAYLOAD_LENGTH
64
+ end
65
+
66
+ # Check if a value is a raw type which should not be trimmed, truncated
67
+ # or converted to a string
68
+ def self.is_json_raw_type?(value)
69
+ RAW_DATA_TYPES.detect {|klass| value.is_a?(klass)} != nil
70
+ end
71
+
72
+ def self.trim_strings_in_hash(hash, seen=[])
73
+ return {} if seen.include?(hash) || !hash.is_a?(Hash)
74
+ result = hash.each_with_object({}) do |(key, value), reduced_hash|
75
+ if reduced_value = trim_strings_in_value(value, seen)
76
+ reduced_hash[key] = reduced_value
77
+ end
78
+ end
79
+ seen << hash
80
+ result
81
+ end
82
+
83
+ # If possible, convert the provided object to a string and trim to the
84
+ # maximum allowed string length
85
+ def self.trim_as_string(text)
86
+ return "" unless text.respond_to? :to_s
87
+ text = text.to_s
88
+ if text.length > MAX_STRING_LENGTH
89
+ length = MAX_STRING_LENGTH - TRUNCATION_INFO.length
90
+ text = text.slice(0, length) + TRUNCATION_INFO
91
+ end
92
+ text
93
+ end
94
+
95
+ def self.trim_strings_in_array(collection, seen=[])
96
+ return [] if seen.include?(collection) || !collection.respond_to?(:map)
97
+ result = collection.map {|value| trim_strings_in_value(value, seen)}
98
+ seen << collection
99
+ result
100
+ end
101
+
102
+ def self.truncate_arrays_in_value(value)
103
+ case value
104
+ when Hash
105
+ truncate_arrays_in_hash(value)
106
+ when Array, Set
107
+ truncate_arrays(value)
108
+ else
109
+ value
110
+ end
111
+ end
112
+
113
+ def self.truncate_arrays_in_hash(hash)
114
+ return {} unless hash.is_a?(Hash)
115
+ hash.each_with_object({}) do |(key, value), reduced_hash|
116
+ if reduced_value = truncate_arrays_in_value(value)
117
+ reduced_hash[key] = reduced_value
118
+ end
119
+ end
120
+ end
34
121
  end
35
122
  end
@@ -23,7 +23,13 @@ module Bugsnag::Middleware
23
23
  # Build the clean url (hide the port if it is obvious)
24
24
  url = "#{request.scheme}://#{request.host}"
25
25
  url << ":#{request.port}" unless [80, 443].include?(request.port)
26
- url << Bugsnag::Cleaner.new(notification.configuration.params_filters).clean_url(request.fullpath)
26
+
27
+ # If app is passed a bad URL, this code will crash attempting to clean it
28
+ begin
29
+ url << Bugsnag::Cleaner.new(notification.configuration.params_filters).clean_url(request.fullpath)
30
+ rescue StandardError => stde
31
+ Bugsnag.log "RackRequest - Rescued error while cleaning request.fullpath: #{stde}"
32
+ end
27
33
 
28
34
  headers = {}
29
35
 
@@ -36,16 +36,9 @@ module Bugsnag
36
36
 
37
37
  class << self
38
38
  def deliver_exception_payload(url, payload, configuration=Bugsnag.configuration, delivery_method=nil)
39
-
40
- # If the payload is going to be too long, we trim the hashes to send
41
- # a minimal payload instead
42
- payload_string = ::JSON.dump(payload)
43
- if payload_string.length > 128000
44
- payload[:events] = payload[:events].map {|e| Bugsnag::Helpers.reduce_hash_size(e)}
45
- payload_string = ::JSON.dump(payload)
46
- end
47
-
48
- Bugsnag::Delivery[delivery_method || configuration.delivery_method].deliver(url, payload_string, configuration)
39
+ payload_string = ::JSON.dump(Bugsnag::Helpers.trim_if_needed(payload))
40
+ delivery_method = delivery_method || configuration.delivery_method
41
+ Bugsnag::Delivery[delivery_method].deliver(url, payload_string, configuration)
49
42
  end
50
43
  end
51
44
 
@@ -7,6 +7,8 @@ require "bugsnag/middleware/rack_request"
7
7
 
8
8
  module Bugsnag
9
9
  class Railtie < Rails::Railtie
10
+ cattr_accessor :running_as_dependency
11
+
10
12
  rake_tasks do
11
13
  require "bugsnag/rake"
12
14
  load "bugsnag/tasks/bugsnag.rake"
@@ -49,7 +51,7 @@ module Bugsnag
49
51
  ActiveRecord::Base.send(:include, Bugsnag::Rails::ActiveRecordRescue)
50
52
  end
51
53
 
52
- Bugsnag.configuration.app_type = "rails"
54
+ Bugsnag.configuration.app_type = "rails" unless Bugsnag::Railtie.running_as_dependency
53
55
  end
54
56
 
55
57
  # Configure params_filters after initialization, so that rails initializers
@@ -36,3 +36,7 @@ end
36
36
 
37
37
  Bugsnag.configuration.internal_middleware.use(Bugsnag::Middleware::Sidekiq)
38
38
  Bugsnag.configuration.app_type = "sidekiq"
39
+
40
+ if defined?(::Sidekiq::CLI) && defined?(Bugsnag::Railtie)
41
+ Bugsnag::Railtie.running_as_dependency = true
42
+ end
@@ -1,30 +1,119 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  require 'spec_helper'
4
+ require 'set'
4
5
 
5
6
  describe Bugsnag::Helpers do
6
- it "reduces hash size correctly" do
7
- meta_data = {
8
- :key_one => "this should not be truncated",
9
- :key_two => ""
10
- }
11
7
 
12
- 1000.times {|i| meta_data[:key_two] += "this should be truncated " }
8
+ describe "trim_if_needed" do
13
9
 
14
- expect(meta_data[:key_two].length).to be > 4096
10
+ context "payload length is less than allowed" do
15
11
 
16
- meta_data_return = Bugsnag::Helpers.reduce_hash_size meta_data
12
+ it "does not change strings" do
13
+ value = SecureRandom.hex(4096)
14
+ expect(Bugsnag::Helpers.trim_if_needed(value)).to be value
15
+ end
17
16
 
18
- expect(meta_data_return[:key_one].length).to eq(28)
19
- expect(meta_data_return[:key_one]).to eq("this should not be truncated")
17
+ it "does not change arrays" do
18
+ value = 1000.times.map {|i| "#{i} - #{i + 1}" }
19
+ expect(Bugsnag::Helpers.trim_if_needed(value)).to be value
20
+ end
20
21
 
21
- expect(meta_data_return[:key_two].length).to eq(4107)
22
- expect(meta_data_return[:key_two].match(/\[TRUNCATED\]$/).nil?).to eq(false)
22
+ it "does not change hashes" do
23
+ value = Hash[*1000.times.map{|i| ["#{i}", i]}.flatten]
24
+ expect(Bugsnag::Helpers.trim_if_needed(value)).to be value
25
+ end
26
+ end
23
27
 
24
- expect(meta_data[:key_two].length).to be > 4096
25
- expect(meta_data[:key_two].match(/\[TRUNCATED\]$/).nil?).to eq(true)
28
+ context "payload length is greater than allowed" do
26
29
 
27
- expect(meta_data[:key_one].length).to eq(28)
28
- expect(meta_data[:key_one]).to eq("this should not be truncated")
30
+ context "value is a String" do
31
+ it "trims length" do
32
+ value = Bugsnag::Helpers.trim_if_needed(SecureRandom.hex(500_000/2))
33
+ expect(::JSON.dump(value.length).length).to be < Bugsnag::Helpers::MAX_STRING_LENGTH
34
+ end
35
+ end
36
+
37
+ context "value is an Array" do
38
+ it "trims nested string contents" do
39
+ value = [[30.times.map {|i| SecureRandom.hex(8192) }]]
40
+ json = ::JSON.dump(Bugsnag::Helpers.trim_if_needed(value))
41
+ expect(json.length).to be < Bugsnag::Helpers::MAX_PAYLOAD_LENGTH
42
+ end
43
+
44
+ it "trims string contents" do
45
+ value = 30.times.map {|i| SecureRandom.hex(8192) }
46
+ json = ::JSON.dump(Bugsnag::Helpers.trim_if_needed(value))
47
+ expect(json.length).to be < Bugsnag::Helpers::MAX_PAYLOAD_LENGTH
48
+ end
49
+ end
50
+
51
+ context "value is a Set" do
52
+ it "trims string contents" do
53
+ value = Set.new(30.times.map {|i| SecureRandom.hex(8192) })
54
+ json = ::JSON.dump(Bugsnag::Helpers.trim_if_needed(value))
55
+ expect(json.length).to be < Bugsnag::Helpers::MAX_PAYLOAD_LENGTH
56
+ end
57
+ end
58
+
59
+ context "value can be converted to a String" do
60
+ it "converts to a string and trims" do
61
+ value = Set.new(30_000.times.map {|i| Bugsnag::Helpers })
62
+ json = ::JSON.dump(Bugsnag::Helpers.trim_if_needed(value))
63
+ expect(json.length).to be < Bugsnag::Helpers::MAX_PAYLOAD_LENGTH
64
+ end
65
+ end
66
+
67
+ context "value is a Hash" do
68
+
69
+ before(:each) do
70
+ @metadata = {
71
+ :short_string => "this should not be truncated",
72
+ :long_string => 10000.times.map {|i| "should truncate" }.join(""),
73
+ :long_string_ary => 30.times.map {|i| SecureRandom.hex(8192) }
74
+ }
75
+
76
+ @trimmed_metadata = Bugsnag::Helpers.trim_if_needed @metadata
77
+ end
78
+
79
+ it "does not trim short values" do
80
+ expect(@trimmed_metadata[:short_string]).to eq @metadata[:short_string]
81
+ end
82
+
83
+ it "trims long string values" do
84
+ expect(@trimmed_metadata[:long_string].length).to eq(Bugsnag::Helpers::MAX_STRING_LENGTH)
85
+ expect(@trimmed_metadata[:long_string].match(/\[TRUNCATED\]$/)).to_not be_nil
86
+ end
87
+
88
+ it "trims nested long string values" do
89
+ @trimmed_metadata[:long_string_ary].each do |str|
90
+ expect(str.match(/\[TRUNCATED\]$/)).to_not be_nil
91
+ expect(str.length).to eq(Bugsnag::Helpers::MAX_STRING_LENGTH)
92
+ end
93
+ end
94
+
95
+ it "does not change the argument value" do
96
+ expect(@metadata[:long_string].length).to be > Bugsnag::Helpers::MAX_STRING_LENGTH
97
+ expect(@metadata[:long_string].match(/\[TRUNCATED\]$/)).to be_nil
98
+ expect(@metadata[:short_string].length).to eq(28)
99
+ expect(@metadata[:short_string]).to eq("this should not be truncated")
100
+ expect(@trimmed_metadata[:long_string_ary].length).to eq(30)
101
+ end
102
+ end
103
+
104
+ context "and trimmed strings are not enough" do
105
+ it "truncates long arrays" do
106
+ value = 100.times.map {|i| SecureRandom.hex(8192) }
107
+ trimmed_value = Bugsnag::Helpers.trim_if_needed(value)
108
+ expect(trimmed_value.length).to be > 0
109
+ trimmed_value.each do |str|
110
+ expect(str.match(/\[TRUNCATED\]$/)).to_not be_nil
111
+ expect(str.length).to eq(Bugsnag::Helpers::MAX_STRING_LENGTH)
112
+ end
113
+
114
+ expect(::JSON.dump(trimmed_value).length).to be < Bugsnag::Helpers::MAX_PAYLOAD_LENGTH
115
+ end
116
+ end
117
+ end
29
118
  end
30
119
  end
@@ -289,6 +289,20 @@ describe Bugsnag::Notification do
289
289
  }
290
290
  end
291
291
 
292
+ it "truncate large stacktraces before sending" do
293
+ ex = BugsnagTestException.new("It crashed")
294
+ stacktrace = []
295
+ 5000.times {|i| stacktrace.push("/Some/path/rspec/example.rb:113:in `instance_eval'")}
296
+ ex.set_backtrace(stacktrace)
297
+ Bugsnag.notify(ex)
298
+
299
+ expect(Bugsnag).to have_sent_notification{ |payload|
300
+ # Truncated body should be no bigger than
301
+ # 400 stacktrace lines * approx 60 chars per line + rest of payload (20000)
302
+ expect(::JSON.dump(payload).length).to be < 400*60 + 20000
303
+ }
304
+ end
305
+
292
306
  it "accepts a severity in overrides" do
293
307
  Bugsnag.notify(BugsnagTestException.new("It crashed"), {
294
308
  :severity => "info"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bugsnag
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.0
4
+ version: 4.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - James Smith
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-03-09 00:00:00.000000000 Z
11
+ date: 2016-04-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake