bugsnag 4.0.0 → 4.0.1

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