rollbar 2.12.0 → 2.13.0

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.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +33 -6
  3. data/README.md +58 -8
  4. data/docs/configuration.md +12 -0
  5. data/gemfiles/rails30.gemfile +1 -0
  6. data/gemfiles/rails31.gemfile +1 -0
  7. data/gemfiles/rails32.gemfile +1 -0
  8. data/gemfiles/rails40.gemfile +3 -0
  9. data/gemfiles/rails41.gemfile +1 -0
  10. data/gemfiles/rails42.gemfile +7 -1
  11. data/gemfiles/rails50.gemfile +2 -1
  12. data/gemfiles/ruby_1_8_and_1_9_2.gemfile +3 -1
  13. data/lib/rollbar.rb +70 -654
  14. data/lib/rollbar/configuration.rb +32 -0
  15. data/lib/rollbar/item.rb +16 -6
  16. data/lib/rollbar/item/backtrace.rb +26 -17
  17. data/lib/rollbar/item/frame.rb +112 -0
  18. data/lib/rollbar/middleware/js.rb +39 -35
  19. data/lib/rollbar/middleware/rails/rollbar.rb +3 -3
  20. data/lib/rollbar/notifier.rb +645 -0
  21. data/lib/rollbar/plugins/delayed_job/job_data.rb +40 -21
  22. data/lib/rollbar/plugins/rails.rb +2 -2
  23. data/lib/rollbar/plugins/rake.rb +32 -6
  24. data/lib/rollbar/plugins/resque.rb +11 -0
  25. data/lib/rollbar/plugins/resque/failure.rb +39 -0
  26. data/lib/rollbar/plugins/validations.rb +10 -0
  27. data/lib/rollbar/request_data_extractor.rb +36 -18
  28. data/lib/rollbar/scrubbers/params.rb +2 -1
  29. data/lib/rollbar/truncation.rb +1 -1
  30. data/lib/rollbar/truncation/frames_strategy.rb +2 -1
  31. data/lib/rollbar/truncation/min_body_strategy.rb +2 -1
  32. data/lib/rollbar/truncation/strings_strategy.rb +1 -1
  33. data/lib/rollbar/version.rb +1 -1
  34. data/spec/controllers/home_controller_spec.rb +13 -24
  35. data/spec/delayed/backend/test.rb +1 -0
  36. data/spec/requests/home_spec.rb +1 -1
  37. data/spec/rollbar/configuration_spec.rb +22 -0
  38. data/spec/rollbar/item/backtrace_spec.rb +26 -0
  39. data/spec/rollbar/item/frame_spec.rb +267 -0
  40. data/spec/rollbar/item_spec.rb +27 -2
  41. data/spec/rollbar/middleware/js_spec.rb +23 -0
  42. data/spec/rollbar/middleware/sinatra_spec.rb +7 -7
  43. data/spec/rollbar/notifier_spec.rb +43 -0
  44. data/spec/rollbar/plugins/delayed_job/{job_data.rb → job_data_spec.rb} +15 -2
  45. data/spec/rollbar/plugins/rack_spec.rb +7 -7
  46. data/spec/rollbar/plugins/rake_spec.rb +1 -2
  47. data/spec/rollbar/plugins/resque/failure_spec.rb +36 -0
  48. data/spec/rollbar/request_data_extractor_spec.rb +103 -1
  49. data/spec/rollbar/truncation/min_body_strategy_spec.rb +1 -1
  50. data/spec/rollbar/truncation/strings_strategy_spec.rb +2 -2
  51. data/spec/rollbar_bc_spec.rb +4 -4
  52. data/spec/rollbar_spec.rb +99 -37
  53. data/spec/spec_helper.rb +2 -2
  54. data/spec/support/notifier_helpers.rb +2 -0
  55. metadata +16 -4
@@ -1,4 +1,5 @@
1
1
  require 'ostruct'
2
+ require 'delayed_job'
2
3
  require 'delayed/backend/base'
3
4
 
4
5
  # This code is taken from delayed_job/spec/delayed/backend/test.rb.
@@ -32,7 +32,7 @@ describe HomeController do
32
32
  ActionDispatch::Cookies::CookieJar.send(:define_method, cookie_method_name, broken_cookie_method)
33
33
  end
34
34
 
35
- after(:each) do
35
+ after do
36
36
  ActionDispatch::Cookies::CookieJar.send(:define_method, cookie_method_name, original_cookie_method)
37
37
  end
38
38
 
@@ -21,4 +21,26 @@ describe Rollbar::Configuration do
21
21
  expect(subject.async_handler).to be_eql(Rollbar::Delay::Resque)
22
22
  end
23
23
  end
24
+
25
+ describe '#merge' do
26
+ it 'returns a new object with overrided values' do
27
+ subject.environment = 'foo'
28
+
29
+ new_config = subject.merge(:environment => 'bar')
30
+
31
+ expect(new_config).not_to be(subject)
32
+ expect(new_config.environment).to be_eql('bar')
33
+ end
34
+ end
35
+
36
+ describe '#merge!' do
37
+ it 'returns the same object with overrided values' do
38
+ subject.environment = 'foo'
39
+
40
+ new_config = subject.merge!(:environment => 'bar')
41
+
42
+ expect(new_config).to be(subject)
43
+ expect(new_config.environment).to be_eql('bar')
44
+ end
45
+ end
24
46
  end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+ require 'tempfile'
3
+ require 'rollbar/item/backtrace'
4
+
5
+ describe Rollbar::Item::Backtrace do
6
+ describe '#get_file_lines' do
7
+ subject { described_class.new(exception) }
8
+
9
+ let(:exception) { Exception.new }
10
+ let(:file) { Tempfile.new('foo') }
11
+
12
+ before do
13
+ File.open(file.path, 'w') do |f|
14
+ f << "foo\nbar"
15
+ end
16
+ end
17
+
18
+ it 'returns the lines of the file' do
19
+ lines = subject.get_file_lines(file.path)
20
+
21
+ expect(lines.size).to be_eql(2)
22
+ expect(lines[0]).to be_eql('foo')
23
+ expect(lines[1]).to be_eql('bar')
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,267 @@
1
+ require 'spec_helper'
2
+ require 'tempfile'
3
+ require 'rollbar/item/backtrace'
4
+ require 'rollbar/item/frame'
5
+
6
+ describe Rollbar::Item::Frame do
7
+ subject { described_class.new(backtrace, frame, options) }
8
+
9
+ let(:backtrace) { double('backtrace') }
10
+ let(:options) { {} }
11
+
12
+ describe '#to_h' do
13
+ context 'with a frame that is not a valid frame' do
14
+ let(:frame) { 'this frame is not valid' }
15
+
16
+ it 'return an unknown frame value' do
17
+ expected_result = {
18
+ :filename => '<unknown>',
19
+ :lineno => 0,
20
+ :method => frame
21
+ }
22
+
23
+ result = subject.to_h
24
+ expect(result).to be_eql(expected_result)
25
+ end
26
+ end
27
+
28
+ context 'with valid frame' do
29
+ let(:file) do
30
+ <<-END
31
+ foo1
32
+ foo2
33
+ foo3
34
+ foo4
35
+ foo5
36
+ foo6
37
+ foo7
38
+ foo8
39
+ foo9
40
+ foo10
41
+ foo11
42
+ foo12
43
+ foo13
44
+ END
45
+ end
46
+ let(:filepath) do
47
+ '/var/www/rollbar/playground/rails4.2/vendor/bundle/gems/actionpack-4.2.0/lib/action_controller/metal/implicit_render.rb'
48
+ end
49
+ let(:frame) do
50
+ "#{filepath}:7:in `send_action'"
51
+ end
52
+ let(:options) do
53
+ { :configuration => configuration }
54
+ end
55
+
56
+ before do
57
+ allow(backtrace).to receive(:get_file_lines).with(filepath).and_return(file.split("\n"))
58
+ end
59
+
60
+ context 'with send_extra_frame_data = :none' do
61
+ let(:configuration) do
62
+ double('configuration',
63
+ :send_extra_frame_data => :none,
64
+ :root => '/var/www')
65
+ end
66
+
67
+ it 'just return the filename, lineno and method' do
68
+ expected_result = {
69
+ :filename => filepath,
70
+ :lineno => 7,
71
+ :method => 'send_action'
72
+ }
73
+
74
+ expect(subject.to_h).to be_eql(expected_result)
75
+ end
76
+ end
77
+
78
+ context 'with send_extra_frame_data = :all' do
79
+ let(:configuration) do
80
+ double('configuration',
81
+ :send_extra_frame_data => :all,
82
+ :root => '/var/www')
83
+ end
84
+
85
+ it 'returns also code and context' do
86
+ expected_result = {
87
+ :filename => filepath,
88
+ :lineno => 7,
89
+ :method => 'send_action',
90
+ :code => 'foo7',
91
+ :context => {
92
+ :pre => %w(foo3 foo4 foo5 foo6),
93
+ :post => %w(foo8 foo9 foo10 foo11)
94
+ }
95
+ }
96
+
97
+ expect(subject.to_h).to be_eql(expected_result)
98
+ end
99
+
100
+ context 'if there is not lines in the file' do
101
+ let(:file) do
102
+ ''
103
+ end
104
+ it 'just returns the basic data' do
105
+ expected_result = {
106
+ :filename => filepath,
107
+ :lineno => 7,
108
+ :method => 'send_action'
109
+ }
110
+
111
+ expect(subject.to_h).to be_eql(expected_result)
112
+ end
113
+ end
114
+
115
+ context 'if the file couldnt be read' do
116
+ before do
117
+ allow(backtrace).to receive(:get_file_lines).with(filepath).and_return(nil)
118
+ end
119
+
120
+ it 'just returns the basic data' do
121
+ expected_result = {
122
+ :filename => filepath,
123
+ :lineno => 7,
124
+ :method => 'send_action'
125
+ }
126
+
127
+ expect(subject.to_h).to be_eql(expected_result)
128
+ end
129
+ end
130
+ end
131
+
132
+ context 'with send_extra_frame_data = :app' do
133
+ context 'with frame outside the root' do
134
+ let(:configuration) do
135
+ double('configuration',
136
+ :send_extra_frame_data => :app,
137
+ :root => '/outside/project',
138
+ :project_gem_paths => [])
139
+ end
140
+
141
+ it 'just returns the basic frame data' do
142
+ expected_result = {
143
+ :filename => filepath,
144
+ :lineno => 7,
145
+ :method => 'send_action'
146
+ }
147
+
148
+ expect(subject.to_h).to be_eql(expected_result)
149
+ end
150
+ end
151
+
152
+ context 'with frame inside project_gem_paths' do
153
+ let(:configuration) do
154
+ double('configuration',
155
+ :send_extra_frame_data => :app,
156
+ :root => '/var/outside/',
157
+ :project_gem_paths => ['/var/www/'])
158
+ end
159
+
160
+ it 'returns also context and code data' do
161
+ expected_result = {
162
+ :filename => filepath,
163
+ :lineno => 7,
164
+ :method => 'send_action',
165
+ :code => 'foo7',
166
+ :context => {
167
+ :pre => %w(foo3 foo4 foo5 foo6),
168
+ :post => %w(foo8 foo9 foo10 foo11)
169
+ }
170
+ }
171
+
172
+ expect(subject.to_h).to be_eql(expected_result)
173
+ end
174
+ end
175
+
176
+ context 'and frame inside app root' do
177
+ let(:configuration) do
178
+ double('configuration',
179
+ :send_extra_frame_data => :app,
180
+ :root => '/var/www',
181
+ :project_gem_paths => [])
182
+ end
183
+
184
+ it 'returns also the context and code data' do
185
+ expected_result = {
186
+ :filename => filepath,
187
+ :lineno => 7,
188
+ :method => 'send_action',
189
+ :code => 'foo7',
190
+ :context => {
191
+ :pre => %w(foo3 foo4 foo5 foo6),
192
+ :post => %w(foo8 foo9 foo10 foo11)
193
+ }
194
+ }
195
+
196
+ expect(subject.to_h).to be_eql(expected_result)
197
+ end
198
+
199
+ context 'but inside Gem.path' do
200
+ let(:configuration) do
201
+ double('configuration',
202
+ :send_extra_frame_data => :app,
203
+ :root => '/var/www/',
204
+ :project_gem_paths => [])
205
+ end
206
+
207
+ before do
208
+ allow(Gem).to receive(:path).and_return(['/var/www/rollbar'])
209
+ end
210
+
211
+ it 'just returns also the basic data' do
212
+ expected_result = {
213
+ :filename => filepath,
214
+ :lineno => 7,
215
+ :method => 'send_action'
216
+ }
217
+
218
+ expect(subject.to_h).to be_eql(expected_result)
219
+ end
220
+ end
221
+
222
+ context 'having less pre lines than maximum' do
223
+ let(:frame) do
224
+ "#{filepath}:3:in `send_action'"
225
+ end
226
+
227
+ it 'returns up to 2 pre lines' do
228
+ expected_result = {
229
+ :filename => filepath,
230
+ :lineno => 3,
231
+ :method => 'send_action',
232
+ :code => 'foo3',
233
+ :context => {
234
+ :pre => %w(foo1 foo2),
235
+ :post => %w(foo4 foo5 foo6 foo7)
236
+ }
237
+ }
238
+
239
+ expect(subject.to_h).to be_eql(expected_result)
240
+ end
241
+ end
242
+
243
+ context 'having less post lines than maximum' do
244
+ let(:frame) do
245
+ "#{filepath}:11:in `send_action'"
246
+ end
247
+
248
+ it 'returns up to 2 post lines' do
249
+ expected_result = {
250
+ :filename => filepath,
251
+ :lineno => 11,
252
+ :method => 'send_action',
253
+ :code => 'foo11',
254
+ :context => {
255
+ :pre => %w(foo7 foo8 foo9 foo10),
256
+ :post => %w(foo12 foo13)
257
+ }
258
+ }
259
+
260
+ expect(subject.to_h).to be_eql(expected_result)
261
+ end
262
+ end
263
+ end
264
+ end
265
+ end
266
+ end
267
+ end
@@ -646,11 +646,36 @@ describe Rollbar::Item do
646
646
  end
647
647
 
648
648
  it 'calls Notifier#send_failsafe and logs the error' do
649
- expect(notifier).to receive(:send_failsafe)
650
- expect(logger).to receive(:error)
649
+ original_size = Rollbar::JSON.dump(payload).bytesize
650
+ final_size = Rollbar::Truncation.truncate(payload.clone).bytesize
651
+ # final_size = original_size
652
+ rollbar_message = "Could not send payload due to it being too large after truncating attempts. Original size: #{original_size} Final size: #{final_size}"
653
+ uuid = payload['data']['uuid']
654
+ host = payload['data']['server']['host']
655
+ log_message = "[Rollbar] Payload too large to be sent for UUID #{uuid}: #{Rollbar::JSON.dump(payload)}"
656
+
657
+ expect(notifier).to receive(:send_failsafe).with(rollbar_message, nil, uuid, host)
658
+ expect(logger).to receive(:error).with(log_message)
651
659
 
652
660
  item.dump
653
661
  end
662
+
663
+ context 'with missing server data' do
664
+ it 'calls Notifier#send_failsafe and logs the error' do
665
+ payload['data'].delete('server')
666
+ original_size = Rollbar::JSON.dump(payload).bytesize
667
+ final_size = Rollbar::Truncation.truncate(payload.clone).bytesize
668
+ # final_size = original_size
669
+ rollbar_message = "Could not send payload due to it being too large after truncating attempts. Original size: #{original_size} Final size: #{final_size}"
670
+ uuid = payload['data']['uuid']
671
+ log_message = "[Rollbar] Payload too large to be sent for UUID #{uuid}: #{Rollbar::JSON.dump(payload)}"
672
+
673
+ expect(notifier).to receive(:send_failsafe).with(rollbar_message, nil, uuid, nil)
674
+ expect(logger).to receive(:error).with(log_message)
675
+
676
+ item.dump
677
+ end
678
+ end
654
679
  end
655
680
  end
656
681
  end
@@ -107,6 +107,10 @@ END
107
107
  before do
108
108
  Object.const_set('SecureHeaders', Module.new)
109
109
  SecureHeaders.const_set('VERSION', '3.0.0')
110
+ SecureHeaders.const_set('Configuration', Module.new {
111
+ def self.get
112
+ end
113
+ })
110
114
  allow(SecureHeaders).to receive(:content_security_policy_script_nonce) { 'lorem-ipsum-nonce' }
111
115
  end
112
116
 
@@ -115,13 +119,32 @@ END
115
119
  end
116
120
 
117
121
  it 'renders the snippet and config in the response with nonce in script tag when SecureHeaders installed' do
122
+ secure_headers_config = double(:configuration, :current_csp => {})
123
+ allow(SecureHeaders::Configuration).to receive(:get).and_return(secure_headers_config)
118
124
  res_status, res_headers, response = subject.call(env)
125
+
119
126
  new_body = response.body.join
120
127
 
121
128
  expect(new_body).to include('<script type="text/javascript" nonce="lorem-ipsum-nonce">')
122
129
  expect(new_body).to include("var _rollbarConfig = #{config[:options].to_json};")
123
130
  expect(new_body).to include(snippet)
124
131
  end
132
+
133
+ it 'renders the snippet in the response without nonce if SecureHeaders script_src includes \'unsafe-inline\'' do
134
+ secure_headers_config = double(:configuration, :current_csp => {
135
+ :script_src => %w('unsafe-inline')
136
+ })
137
+ allow(SecureHeaders::Configuration).to receive(:get).and_return(secure_headers_config)
138
+
139
+ res_status, res_headers, response = subject.call(env)
140
+ new_body = response.body.join
141
+
142
+ expect(new_body).to include('<script type="text/javascript">')
143
+ expect(new_body).to include("var _rollbarConfig = #{config[:options].to_json};")
144
+ expect(new_body).to include(snippet)
145
+
146
+ SecureHeaders.send(:remove_const, 'Configuration')
147
+ end
125
148
  end
126
149
 
127
150
  context 'having a html 200 response and SecureHeaders < 3.0.0 defined' do
@@ -115,7 +115,7 @@ describe Rollbar::Middleware::Sinatra, :reconfigure_notifier => true do
115
115
  get '/foo', params
116
116
  end.to raise_error(exception)
117
117
 
118
- expect(Rollbar.last_report[:request][:params]).to be_eql(params)
118
+ expect(Rollbar.last_report[:request][:GET]).to be_eql(params)
119
119
  end
120
120
  end
121
121
 
@@ -132,7 +132,7 @@ describe Rollbar::Middleware::Sinatra, :reconfigure_notifier => true do
132
132
  post '/crash_post', params
133
133
  end.to raise_error(exception)
134
134
 
135
- expect(Rollbar.last_report[:request][:params]).to be_eql(params)
135
+ expect(Rollbar.last_report[:request][:POST]).to be_eql(params)
136
136
  end
137
137
  end
138
138
 
@@ -149,7 +149,7 @@ describe Rollbar::Middleware::Sinatra, :reconfigure_notifier => true do
149
149
  post '/crash_post', params.to_json, { 'CONTENT_TYPE' => 'application/json' }
150
150
  end.to raise_error(exception)
151
151
 
152
- expect(Rollbar.last_report[:request][:params]).to be_eql(params)
152
+ expect(Rollbar.last_report[:request][:body]).to be_eql(params.to_json)
153
153
  end
154
154
 
155
155
  it 'appears in the sent payload when the accepts header contains json' do
@@ -157,16 +157,16 @@ describe Rollbar::Middleware::Sinatra, :reconfigure_notifier => true do
157
157
  post '/crash_post', params, { 'ACCEPT' => 'application/vnd.github.v3+json' }
158
158
  end.to raise_error(exception)
159
159
 
160
- expect(Rollbar.last_report[:request][:params]).to be_eql(params)
160
+ expect(Rollbar.last_report[:request][:POST]).to be_eql(params)
161
161
  end
162
162
  end
163
163
 
164
- it 'resets the notifier in every request' do
164
+ it 'resets the notifier scope in every request' do
165
165
  get '/bar'
166
- id1 = Rollbar.notifier.object_id
166
+ id1 = Rollbar.scope_object.object_id
167
167
 
168
168
  get '/bar'
169
- id2 = Rollbar.notifier.object_id
169
+ id2 = Rollbar.scope_object.object_id
170
170
 
171
171
  expect(id1).not_to be_eql(id2)
172
172
  end