rollbar 2.12.0 → 2.13.0

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