rollbar 2.17.0 → 2.18.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.
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