rollbar 2.17.0 → 2.18.0

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: 80c4468fc2e5243d35fc86e2f4787012d996ef58
4
- data.tar.gz: 3469d5b5650578975879fa8962f499d7a1aea52c
3
+ metadata.gz: e357b034a0d0686c4ff7e2df37e4500b964ff1d8
4
+ data.tar.gz: 8af0947410c8afd1a1b8e7d6691cc57651f0906f
5
5
  SHA512:
6
- metadata.gz: 20890794849d8a1db3b4cf0947cf84e9c824d60e81ae4c65ebeca3cf6705357bf90b98d21185d8aa038d378f420799131884529a9eb525ca3630c4c1ecae7d3b
7
- data.tar.gz: 62b215e34d6b9d30123bd79069e59fd284bf19ba870db97a78d88e9c91414b67daf32c9ebb9e910305b3baa9428954815c17c998504e7a12c4ba47e0bd8b0750
6
+ metadata.gz: 1d320a75cf677e32ab3bf635bdff22235b39b44e4dce406b74151a2bbd9e344fb0113a2130061e93c1cd473a1f27964c97ad2b68c8a8b20b69766b9dd951cdb8
7
+ data.tar.gz: 371056dcb28f6247fafbd6404e1a39f873660060758393119b588cd15e9c8b67f349407422ca31b5ce163dd48a1b356ad665fcbb8d42a31ebcf81b3701150961
data/Gemfile CHANGED
@@ -42,5 +42,6 @@ gem 'redis'
42
42
  gem 'resque'
43
43
  gem 'shoryuken'
44
44
  gem 'sinatra'
45
+ gem 'webmock'
45
46
 
46
47
  gemspec
@@ -40,7 +40,6 @@ end
40
40
 
41
41
  # We need last sinatra that uses rack 2.x
42
42
  gem 'sinatra', :git => 'https://github.com/sinatra/sinatra'
43
-
44
43
  gem 'codeclimate-test-reporter', :group => :test, :require => nil
45
44
  gem 'database_cleaner', '~> 1.x'
46
45
  gem 'delayed_job', :require => false
@@ -36,7 +36,7 @@ gem 'database_cleaner', '~> 1.0.0'
36
36
  gem 'delayed_job', '4.1.3', :require => false
37
37
  gem 'genspec', '= 0.2.8'
38
38
  gem 'girl_friday', '>= 0.11.1'
39
- gem 'redis'
39
+ gem 'redis', '< 3.3.5'
40
40
  gem 'resque'
41
41
  gem 'sinatra'
42
42
 
@@ -128,6 +128,10 @@ module Rollbar
128
128
  @proxy = nil
129
129
  @collect_user_ip = true
130
130
  @anonymize_user_ip = false
131
+ @hooks = {
132
+ :on_error_response => nil, # params: response
133
+ :on_report_internal_error => nil, #params: exception
134
+ }
131
135
  end
132
136
 
133
137
  def initialize_copy(orig)
@@ -259,5 +263,21 @@ module Rollbar
259
263
  def logger
260
264
  @logger ||= default_logger.call
261
265
  end
266
+
267
+ def hook(symbol, &block)
268
+ if @hooks.has_key?(symbol)
269
+ if block_given?
270
+ @hooks[symbol] = block
271
+ else
272
+ @hooks[symbol]
273
+ end
274
+ else
275
+ raise StandardError.new "Hook :" + symbol.to_s + " is not supported by Rollbar SDK."
276
+ end
277
+ end
278
+
279
+ def execute_hook(symbol, *args)
280
+ hook(symbol).call(*args) if hook(symbol).is_a?(Proc)
281
+ end
262
282
  end
263
283
  end
@@ -36,6 +36,8 @@ module Rollbar
36
36
  end
37
37
  end
38
38
 
39
+ private
40
+
39
41
  def enabled?
40
42
  !!config[:enabled]
41
43
  end
@@ -155,10 +157,86 @@ module Rollbar
155
157
  end
156
158
 
157
159
  def append_nonce?
158
- defined?(::SecureHeaders) && ::SecureHeaders.respond_to?(:content_security_policy_script_nonce) &&
159
- defined?(::SecureHeaders::Configuration) &&
160
- !::SecureHeaders::Configuration.default.csp.opt_out? &&
161
- !::SecureHeaders::Configuration.default.current_csp[:script_src].to_a.include?("'unsafe-inline'")
160
+ secure_headers.append_nonce?
161
+ end
162
+
163
+ def secure_headers
164
+ return SecureHeadersFalse.new unless defined?(::SecureHeaders::Configuration)
165
+
166
+ config = ::SecureHeaders::Configuration
167
+
168
+ secure_headers_cls = nil
169
+
170
+ if !::SecureHeaders::respond_to?(:content_security_policy_script_nonce)
171
+ secure_headers_cls = SecureHeadersFalse
172
+ elsif config.respond_to?(:get)
173
+ secure_headers_cls = SecureHeaders3To5
174
+ elsif config.dup.respond_to?(:csp)
175
+ secure_headers_cls = SecureHeaders6
176
+ else
177
+ secure_headers_cls = SecureHeadersFalse
178
+ end
179
+
180
+ secure_headers_cls.new
181
+ end
182
+
183
+ class SecureHeadersResolver
184
+ def append_nonce?
185
+ csp_needs_nonce?(find_csp)
186
+ end
187
+
188
+ private
189
+
190
+ def find_csp
191
+ raise NotImplementedError
192
+ end
193
+
194
+ def csp_needs_nonce?(csp)
195
+ !opt_out?(csp) && !unsafe_inline?(csp)
196
+ end
197
+
198
+ def opt_out?(csp)
199
+ raise NotImplementedError
200
+ end
201
+
202
+ def unsafe_inline?(csp)
203
+ csp[:script_src].to_a.include?("'unsafe-inline'")
204
+ end
205
+ end
206
+
207
+ class SecureHeadersFalse < SecureHeadersResolver
208
+ def append_nonce?
209
+ false
210
+ end
211
+ end
212
+
213
+ class SecureHeaders3To5 < SecureHeadersResolver
214
+ private
215
+
216
+ def find_csp
217
+ ::SecureHeaders::Configuration.get.csp
218
+ end
219
+
220
+ def opt_out?(csp)
221
+ if csp.respond_to?(:opt_out?) && csp.opt_out?
222
+ csp.opt_out?
223
+ # secure_headers csp 3.0.x-3.4.x doesn't respond to 'opt_out?'
224
+ elsif defined?(::SecureHeaders::OPT_OUT) && ::SecureHeaders::OPT_OUT.is_a?(Symbol)
225
+ csp == ::SecureHeaders::OPT_OUT
226
+ end
227
+ end
228
+ end
229
+
230
+ class SecureHeaders6 < SecureHeadersResolver
231
+ private
232
+
233
+ def find_csp
234
+ ::SecureHeaders::Configuration.dup.csp
235
+ end
236
+
237
+ def opt_out?(csp)
238
+ csp.opt_out?
239
+ end
162
240
  end
163
241
  end
164
242
  end
@@ -407,11 +407,14 @@ module Rollbar
407
407
  # If that fails, we'll fall back to a more static failsafe response.
408
408
  def report_internal_error(exception)
409
409
  log_error '[Rollbar] Reporting internal error encountered while sending data to Rollbar.'
410
+
411
+ configuration.execute_hook(:on_report_internal_error, exception)
410
412
 
411
413
  begin
412
414
  item = build_item('error', nil, exception, { :internal => true }, nil)
413
415
  rescue => e
414
416
  send_failsafe('build_item in exception_data', e)
417
+ log_error "[Rollbar] Exception: #{exception}"
415
418
  return
416
419
  end
417
420
 
@@ -419,6 +422,7 @@ module Rollbar
419
422
  process_item(item)
420
423
  rescue => e
421
424
  send_failsafe('error in process_item', e)
425
+ log_error "[Rollbar] Item: #{item}"
422
426
  return
423
427
  end
424
428
 
@@ -426,6 +430,7 @@ module Rollbar
426
430
  log_instance_link(item['data'])
427
431
  rescue => e
428
432
  send_failsafe('error logging instance link', e)
433
+ log_error "[Rollbar] Item: #{item}"
429
434
  return
430
435
  end
431
436
  end
@@ -575,6 +580,7 @@ module Rollbar
575
580
  else
576
581
  log_warning "[Rollbar] Got unexpected status code from Rollbar api: #{response.code}"
577
582
  log_info "[Rollbar] Response: #{response.body}"
583
+ configuration.execute_hook(:on_error_response, response)
578
584
  end
579
585
  end
580
586
 
@@ -6,7 +6,8 @@ module Rollbar
6
6
  Rollbar.error(exception,
7
7
  :job => self.class.name,
8
8
  :job_id => job_id,
9
- :use_exception_level_filters => true)
9
+ :use_exception_level_filters => true,
10
+ :arguments => arguments)
10
11
  raise exception
11
12
  end
12
13
  end
@@ -12,7 +12,7 @@ module Rollbar
12
12
  lifecycle.around(:invoke_job, &Delayed::invoke_job_callback)
13
13
  lifecycle.after(:failure) do |_, job, _, _|
14
14
  data = Rollbar::Delayed.build_job_data(job)
15
- ::Rollbar.scope(:request => data).error(job.last_error, :use_exception_level_filters => true) if job.last_error
15
+ ::Rollbar.scope(:request => data).error("Job has failed and won't be retried anymore: " + job.last_error, :use_exception_level_filters => true) if job.last_error
16
16
  end
17
17
  end
18
18
  end
@@ -1,5 +1,3 @@
1
- require 'rollbar'
2
-
3
1
  module Rollbar
4
2
  module RailtieMixin
5
3
  extend ActiveSupport::Concern
@@ -1,3 +1,3 @@
1
1
  module Rollbar
2
- VERSION = '2.17.0'
2
+ VERSION = '2.18.0'
3
3
  end
@@ -43,4 +43,32 @@ describe Rollbar::Configuration do
43
43
  expect(new_config.environment).to be_eql('bar')
44
44
  end
45
45
  end
46
+
47
+ describe '#hook' do
48
+ it "assigns and returns the appropriate hook" do
49
+ subject.hook :on_error_response do
50
+ puts "foo hook"
51
+ end
52
+
53
+ expect(subject.hook(:on_error_response).is_a?(Proc)).to be_eql(true)
54
+ end
55
+
56
+ it "raises a StandardError if requested hook is not supported" do
57
+ expect{ subject.hook(:foo) }.to raise_error(StandardError)
58
+ end
59
+ end
60
+
61
+ describe '#execute_hook' do
62
+ it "executes the approriate hook" do
63
+ bar = "test value"
64
+
65
+ subject.hook :on_error_response do
66
+ bar = "changed value"
67
+ end
68
+
69
+ subject.execute_hook :on_error_response
70
+
71
+ expect(bar).to be_eql("changed value")
72
+ end
73
+ end
46
74
  end
@@ -1,6 +1,50 @@
1
1
  require 'spec_helper'
2
2
  require 'rollbar/middleware/js'
3
3
 
4
+
5
+ shared_examples 'secure_headers' do
6
+ it 'renders the snippet and config in the response with nonce in script tag when SecureHeaders installed' do
7
+ SecureHeadersMocks::CSP.config = {
8
+ :opt_out? => false
9
+ }
10
+
11
+ _, _, response = subject.call(env)
12
+
13
+ new_body = response.body.join
14
+
15
+ expect(new_body).to include('<script type="text/javascript" nonce="lorem-ipsum-nonce">')
16
+ expect(new_body).to include("var _rollbarConfig = #{config[:options].to_json};")
17
+ expect(new_body).to include(snippet)
18
+ end
19
+
20
+ it 'renders the snippet in the response without nonce if SecureHeaders script_src includes \'unsafe-inline\'' do
21
+ SecureHeadersMocks::CSP.config = {
22
+ :opt_out? => false,
23
+ :script_src => %w('unsafe-inline')
24
+ }
25
+
26
+ _, _, response = subject.call(env)
27
+ new_body = response.body.join
28
+
29
+ expect(new_body).to include('<script type="text/javascript">')
30
+ expect(new_body).to include("var _rollbarConfig = #{config[:options].to_json};")
31
+ expect(new_body).to include(snippet)
32
+ end
33
+
34
+ it 'renders the snippet in the response without nonce if SecureHeaders CSP is OptOut' do
35
+ SecureHeadersMocks::CSP.config = {
36
+ :opt_out? => true
37
+ }
38
+
39
+ _, _, response = subject.call(env)
40
+ new_body = response.body.join
41
+
42
+ expect(new_body).to include('<script type="text/javascript">')
43
+ expect(new_body).to include("var _rollbarConfig = #{config[:options].to_json};")
44
+ expect(new_body).to include(snippet)
45
+ end
46
+ end
47
+
4
48
  describe Rollbar::Middleware::Js do
5
49
  subject { described_class.new(app, config) }
6
50
 
@@ -174,61 +218,26 @@ END
174
218
  end
175
219
 
176
220
  before do
177
- Object.const_set('SecureHeaders', Module.new)
178
- SecureHeaders.const_set('VERSION', '3.0.0')
179
- SecureHeaders.const_set('Configuration', Module.new {
180
- def self.default
181
- end
182
- })
183
- allow(SecureHeaders).to receive(:content_security_policy_script_nonce) { 'lorem-ipsum-nonce' }
184
- end
185
-
186
- after do
187
- Object.send(:remove_const, 'SecureHeaders')
221
+ stub_const('::SecureHeaders', secure_headers_mock)
222
+ SecureHeadersMocks::CSP.config = {}
188
223
  end
189
224
 
190
- it 'renders the snippet and config in the response with nonce in script tag when SecureHeaders installed' do
191
- secure_headers_config = double(:configuration, :current_csp => {}, :csp => double(:opt_out? => false))
192
- allow(SecureHeaders::Configuration).to receive(:default).and_return(secure_headers_config)
193
- res_status, res_headers, response = subject.call(env)
225
+ context 'with secure headers 3.0.x-3.4.x' do
226
+ let(:secure_headers_mock) { SecureHeadersMocks::SecureHeaders30 }
194
227
 
195
- new_body = response.body.join
196
-
197
- expect(new_body).to include('<script type="text/javascript" nonce="lorem-ipsum-nonce">')
198
- expect(new_body).to include("var _rollbarConfig = #{config[:options].to_json};")
199
- expect(new_body).to include(snippet)
228
+ include_examples 'secure_headers'
200
229
  end
201
230
 
202
- it 'renders the snippet in the response without nonce if SecureHeaders script_src includes \'unsafe-inline\'' do
203
- secure_headers_config = double(:configuration,
204
- :current_csp => {
205
- :script_src => %w('unsafe-inline')
206
- },
207
- :csp => double(:opt_out? => false))
208
- allow(SecureHeaders::Configuration).to receive(:default).and_return(secure_headers_config)
209
-
210
- res_status, res_headers, response = subject.call(env)
211
- new_body = response.body.join
231
+ context 'with secure headers 3.5' do
232
+ let(:secure_headers_mock) { SecureHeadersMocks::SecureHeaders35 }
212
233
 
213
- expect(new_body).to include('<script type="text/javascript">')
214
- expect(new_body).to include("var _rollbarConfig = #{config[:options].to_json};")
215
- expect(new_body).to include(snippet)
216
-
217
- SecureHeaders.send(:remove_const, 'Configuration')
234
+ include_examples 'secure_headers'
218
235
  end
219
236
 
220
- it 'renders the snippet in the response without nonce if SecureHeaders CSP is OptOut' do
221
- secure_headers_config = double(:configuration, :csp => double(:opt_out? => true))
222
- allow(SecureHeaders::Configuration).to receive(:default).and_return(secure_headers_config)
223
-
224
- res_status, res_headers, response = subject.call(env)
225
- new_body = response.body.join
237
+ context 'with secure headers 6.0' do
238
+ let(:secure_headers_mock) { SecureHeadersMocks::SecureHeaders60 }
226
239
 
227
- expect(new_body).to include('<script type="text/javascript">')
228
- expect(new_body).to include("var _rollbarConfig = #{config[:options].to_json};")
229
- expect(new_body).to include(snippet)
230
-
231
- SecureHeaders.send(:remove_const, 'Configuration')
240
+ include_examples 'secure_headers'
232
241
  end
233
242
  end
234
243
 
@@ -240,16 +249,11 @@ END
240
249
  end
241
250
 
242
251
  before do
243
- Object.const_set('SecureHeaders', Module.new)
244
- SecureHeaders.const_set('VERSION', '2.4.0')
245
- end
246
-
247
- after do
248
- Object.send(:remove_const, 'SecureHeaders')
252
+ stub_const('::SecureHeaders', ::SecureHeadersMocks::SecureHeaders20)
249
253
  end
250
254
 
251
255
  it 'renders the snippet and config in the response without nonce in script tag when too old SecureHeaders installed' do
252
- res_status, res_headers, response = subject.call(env)
256
+ _, _, response = subject.call(env)
253
257
  new_body = response.body.join
254
258
 
255
259
  expect(new_body).to include('<script type="text/javascript">')
@@ -354,10 +358,10 @@ END
354
358
  it 'adds the person data to the configuration' do
355
359
  _, _, response = subject.call(env)
356
360
  new_body = response.body.join
357
-
361
+
358
362
  rollbar_config = new_body[/var _rollbarConfig = (.*);<\/script>/, 1]
359
363
  rollbar_config = JSON.parse(rollbar_config, { :symbolize_names => true})
360
-
364
+
361
365
  expect(rollbar_config).to eql(expected_js_options)
362
366
  end
363
367
 
@@ -9,6 +9,11 @@ describe Rollbar::ActiveJob do
9
9
  include Rollbar::ActiveJob
10
10
 
11
11
  attr_reader :job_id
12
+ attr_accessor :arguments
13
+
14
+ def initialize(*arguments)
15
+ @arguments = arguments
16
+ end
12
17
 
13
18
  def perform(exception, job_id)
14
19
  @job_id = job_id
@@ -21,18 +26,20 @@ describe Rollbar::ActiveJob do
21
26
 
22
27
  let(:exception) { StandardError.new('oh no') }
23
28
  let(:job_id) { "123" }
29
+ let(:argument) { 12 }
24
30
 
25
31
  it "reports the error to Rollbar" do
26
32
  expected_params = {
27
33
  :job => "TestJob",
28
34
  :job_id => job_id,
29
- :use_exception_level_filters => true
35
+ :use_exception_level_filters => true,
36
+ :arguments => [argument]
30
37
  }
31
38
  expect(Rollbar).to receive(:error).with(exception, expected_params)
32
- TestJob.new.perform(exception, job_id) rescue nil
39
+ TestJob.new(argument).perform(exception, job_id) rescue nil
33
40
  end
34
41
 
35
42
  it "reraises the error so the job backend can handle the failure and retry" do
36
- expect { TestJob.new.perform(exception, job_id) }.to raise_error exception
43
+ expect { TestJob.new(argument).perform(exception, job_id) }.to raise_error exception
37
44
  end
38
45
  end
data/spec/rollbar_spec.rb CHANGED
@@ -159,6 +159,65 @@ describe Rollbar do
159
159
  notifier.log('error', exception, extra_data, 'exception description')
160
160
  end
161
161
 
162
+ context 'with :on_error_response hook configured' do
163
+ let!(:notifier) { Rollbar::Notifier.new }
164
+ let(:configuration) do
165
+ config = Rollbar::Configuration.new
166
+ config.access_token = test_access_token
167
+ config.enabled = true
168
+
169
+ config.hook :on_error_response do |response|
170
+ return ":on_error_response executed"
171
+ end
172
+
173
+ config
174
+ end
175
+ let(:message) { 'foo' }
176
+ let(:level) { 'foo' }
177
+
178
+ before do
179
+ notifier.configuration = configuration
180
+ allow_any_instance_of(Net::HTTP).to receive(:request).and_return(OpenStruct.new(:code => 500, :body => "Error"))
181
+ @uri = URI.parse(Rollbar::Configuration::DEFAULT_ENDPOINT)
182
+ end
183
+
184
+ it "calls the :on_error_response hook if response status is not 200" do
185
+ expect(Net::HTTP).to receive(:new).with(@uri.host, @uri.port, nil, nil, nil, nil).and_call_original
186
+ expect(notifier.configuration.hook(:on_error_response)).to receive(:call)
187
+
188
+ notifier.log(level, message)
189
+ end
190
+ end
191
+
192
+ context 'with :on_report_internal_error hook configured' do
193
+ let!(:notifier) { Rollbar::Notifier.new }
194
+ let(:configuration) do
195
+ config = Rollbar::Configuration.new
196
+ config.access_token = test_access_token
197
+ config.enabled = true
198
+
199
+ config.hook :on_report_internal_error do |response|
200
+ return ":on_report_internal_error executed"
201
+ end
202
+
203
+ config
204
+ end
205
+ let(:message) { 'foo' }
206
+ let(:level) { 'foo' }
207
+
208
+ before do
209
+ notifier.configuration = configuration
210
+ end
211
+
212
+ it "calls the :on_report_internal_error hook if" do
213
+ expect(notifier.configuration.hook(:on_report_internal_error)).to receive(:call)
214
+ expect(notifier).to receive(:report) do
215
+ raise StandardError.new
216
+ end
217
+ notifier.log(level, message)
218
+ end
219
+ end
220
+
162
221
  context 'an item with a context' do
163
222
  let(:context) { { :controller => 'ExampleController' } }
164
223
 
@@ -0,0 +1,83 @@
1
+ module SecureHeadersMocks
2
+ NONCE = 'lorem-ipsum-nonce'
3
+
4
+ module CSP
5
+ class << self
6
+ attr_accessor :config
7
+
8
+ def opt_out?
9
+ config[:opt_out?]
10
+ end
11
+
12
+ def [](key)
13
+ config[key]
14
+ end
15
+ end
16
+ end
17
+
18
+ module SecureHeaders20
19
+ end
20
+
21
+ module SecureHeaders30
22
+ OPT_OUT = :opt_out
23
+ class << self
24
+ def content_security_policy_script_nonce(req)
25
+ NONCE
26
+ end
27
+ end
28
+
29
+ module Configuration
30
+ module CSPProxy
31
+ def self.csp
32
+ return OPT_OUT if CSP.opt_out?
33
+
34
+ CSP.config
35
+ end
36
+ end
37
+
38
+ def self.get
39
+ CSPProxy
40
+ end
41
+ end
42
+ end
43
+
44
+ module SecureHeaders35
45
+ class << self
46
+ def content_security_policy_script_nonce(req)
47
+ NONCE
48
+ end
49
+ end
50
+
51
+ module Configuration
52
+ module CSPProxy
53
+ def self.csp
54
+ CSP
55
+ end
56
+ end
57
+
58
+ def self.get
59
+ CSPProxy
60
+ end
61
+ end
62
+ end
63
+
64
+ module SecureHeaders60
65
+ class << self
66
+ def content_security_policy_script_nonce(req)
67
+ NONCE
68
+ end
69
+ end
70
+
71
+ module Configuration
72
+ module CSPProxy
73
+ def self.csp
74
+ CSP
75
+ end
76
+ end
77
+
78
+ def self.dup
79
+ CSPProxy
80
+ end
81
+ end
82
+ end
83
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rollbar
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.17.0
4
+ version: 2.18.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rollbar, Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-08-27 00:00:00.000000000 Z
11
+ date: 2018-09-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: multi_json
@@ -264,6 +264,7 @@ files:
264
264
  - spec/support/matchers.rb
265
265
  - spec/support/notifier_helpers.rb
266
266
  - spec/support/rollbar_api.rb
267
+ - spec/support/secure_headers_mocks.rb
267
268
  - spec/support/shared_contexts.rb
268
269
  homepage: https://rollbar.com
269
270
  licenses:
@@ -416,4 +417,5 @@ test_files:
416
417
  - spec/support/matchers.rb
417
418
  - spec/support/notifier_helpers.rb
418
419
  - spec/support/rollbar_api.rb
420
+ - spec/support/secure_headers_mocks.rb
419
421
  - spec/support/shared_contexts.rb