airbrakeV4rails5 4.3.8

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 (98) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG +1716 -0
  3. data/Gemfile +3 -0
  4. data/Guardfile +6 -0
  5. data/INSTALL +20 -0
  6. data/LICENSE +61 -0
  7. data/README.md +148 -0
  8. data/README_FOR_HEROKU_ADDON.md +102 -0
  9. data/Rakefile +179 -0
  10. data/TESTED_AGAINST +7 -0
  11. data/airbrake.gemspec +41 -0
  12. data/bin/airbrake +12 -0
  13. data/features/metal.feature +34 -0
  14. data/features/rack.feature +60 -0
  15. data/features/rails.feature +324 -0
  16. data/features/rake.feature +33 -0
  17. data/features/sinatra.feature +126 -0
  18. data/features/step_definitions/file_steps.rb +14 -0
  19. data/features/step_definitions/rack_steps.rb +27 -0
  20. data/features/step_definitions/rails_application_steps.rb +267 -0
  21. data/features/step_definitions/rake_steps.rb +22 -0
  22. data/features/support/airbrake_shim.rb.template +11 -0
  23. data/features/support/aruba.rb +5 -0
  24. data/features/support/env.rb +39 -0
  25. data/features/support/matchers.rb +35 -0
  26. data/features/support/rails.rb +156 -0
  27. data/features/support/rake/Rakefile +77 -0
  28. data/features/user_informer.feature +57 -0
  29. data/generators/airbrake/airbrake_generator.rb +94 -0
  30. data/generators/airbrake/lib/insert_commands.rb +34 -0
  31. data/generators/airbrake/lib/rake_commands.rb +24 -0
  32. data/generators/airbrake/templates/airbrake_tasks.rake +25 -0
  33. data/generators/airbrake/templates/capistrano_hook.rb +6 -0
  34. data/generators/airbrake/templates/initializer.rb +4 -0
  35. data/install.rb +1 -0
  36. data/lib/airbrake.rb +191 -0
  37. data/lib/airbrake/backtrace.rb +103 -0
  38. data/lib/airbrake/capistrano.rb +103 -0
  39. data/lib/airbrake/capistrano3.rb +3 -0
  40. data/lib/airbrake/cli/client.rb +76 -0
  41. data/lib/airbrake/cli/options.rb +45 -0
  42. data/lib/airbrake/cli/printer.rb +33 -0
  43. data/lib/airbrake/cli/project.rb +17 -0
  44. data/lib/airbrake/cli/project_factory.rb +33 -0
  45. data/lib/airbrake/cli/runner.rb +49 -0
  46. data/lib/airbrake/cli/validator.rb +8 -0
  47. data/lib/airbrake/configuration.rb +366 -0
  48. data/lib/airbrake/jobs/send_job.rb +7 -0
  49. data/lib/airbrake/notice.rb +411 -0
  50. data/lib/airbrake/rack.rb +64 -0
  51. data/lib/airbrake/rails.rb +45 -0
  52. data/lib/airbrake/rails/action_controller_catcher.rb +32 -0
  53. data/lib/airbrake/rails/controller_methods.rb +146 -0
  54. data/lib/airbrake/rails/error_lookup.rb +35 -0
  55. data/lib/airbrake/rails/middleware.rb +63 -0
  56. data/lib/airbrake/rails3_tasks.rb +126 -0
  57. data/lib/airbrake/railtie.rb +44 -0
  58. data/lib/airbrake/rake_handler.rb +75 -0
  59. data/lib/airbrake/response.rb +29 -0
  60. data/lib/airbrake/sender.rb +213 -0
  61. data/lib/airbrake/shared_tasks.rb +59 -0
  62. data/lib/airbrake/sidekiq.rb +8 -0
  63. data/lib/airbrake/sinatra.rb +40 -0
  64. data/lib/airbrake/tasks.rb +81 -0
  65. data/lib/airbrake/tasks/airbrake.cap +28 -0
  66. data/lib/airbrake/user_informer.rb +36 -0
  67. data/lib/airbrake/utils/params_cleaner.rb +141 -0
  68. data/lib/airbrake/utils/rack_filters.rb +45 -0
  69. data/lib/airbrake/version.rb +3 -0
  70. data/lib/airbrake_tasks.rb +62 -0
  71. data/lib/rails/generators/airbrake/airbrake_generator.rb +155 -0
  72. data/lib/templates/rescue.erb +91 -0
  73. data/rails/init.rb +1 -0
  74. data/resources/README.md +34 -0
  75. data/resources/airbrake_2_4.xsd +89 -0
  76. data/resources/airbrake_3_0.json +52 -0
  77. data/resources/ca-bundle.crt +3376 -0
  78. data/script/integration_test.rb +35 -0
  79. data/test/airbrake_tasks_test.rb +161 -0
  80. data/test/backtrace_test.rb +215 -0
  81. data/test/capistrano_test.rb +44 -0
  82. data/test/configuration_test.rb +303 -0
  83. data/test/controller_methods_test.rb +230 -0
  84. data/test/helper.rb +233 -0
  85. data/test/integration.rb +13 -0
  86. data/test/integration/catcher_test.rb +371 -0
  87. data/test/logger_test.rb +79 -0
  88. data/test/notice_test.rb +494 -0
  89. data/test/notifier_test.rb +288 -0
  90. data/test/params_cleaner_test.rb +204 -0
  91. data/test/rack_test.rb +62 -0
  92. data/test/rails_initializer_test.rb +36 -0
  93. data/test/recursion_test.rb +10 -0
  94. data/test/response_test.rb +18 -0
  95. data/test/sender_test.rb +335 -0
  96. data/test/support/response_shim.xml +4 -0
  97. data/test/user_informer_test.rb +29 -0
  98. metadata +469 -0
@@ -0,0 +1,288 @@
1
+ require File.expand_path '../helper', __FILE__
2
+
3
+ class NotifierTest < Test::Unit::TestCase
4
+
5
+ class OriginalException < Exception
6
+ end
7
+
8
+ class ContinuedException < Exception
9
+ end
10
+
11
+ include DefinesConstants
12
+
13
+ def setup
14
+ super
15
+ reset_config
16
+ end
17
+
18
+ def assert_sent(notice, notice_args)
19
+ assert_received(Airbrake::Notice, :new) {|expect| expect.with(has_entries(notice_args)) }
20
+ assert_received(Airbrake.sender, :send_to_airbrake) {|expect| expect.with(notice) }
21
+ end
22
+
23
+ def set_public_env
24
+ Airbrake.configure { |config| config.environment_name = 'production' }
25
+ end
26
+
27
+ def set_development_env
28
+ Airbrake.configure { |config| config.environment_name = 'development' }
29
+ end
30
+
31
+ should "yield and save a configuration when configuring" do
32
+ yielded_configuration = nil
33
+ Airbrake.configure do |config|
34
+ yielded_configuration = config
35
+ end
36
+
37
+ assert_kind_of Airbrake::Configuration, yielded_configuration
38
+ assert_equal yielded_configuration, Airbrake.configuration
39
+ end
40
+
41
+ should "not remove existing config options when configuring twice" do
42
+ first_config = nil
43
+ Airbrake.configure do |config|
44
+ first_config = config
45
+ end
46
+ Airbrake.configure do |config|
47
+ assert_equal first_config, config
48
+ end
49
+ end
50
+
51
+ should "configure the sender" do
52
+ sender = stub_sender
53
+ Airbrake::Sender.stubs(:new => sender)
54
+ configuration = nil
55
+
56
+ Airbrake.configure { |yielded_config| configuration = yielded_config }
57
+
58
+ assert_received(Airbrake::Sender, :new) { |expect| expect.with(configuration) }
59
+ assert_equal sender, Airbrake.sender
60
+ end
61
+
62
+ should "create and send a notice for an exception" do
63
+ set_public_env
64
+ exception = build_exception
65
+ stub_sender!
66
+ notice = stub_notice!
67
+
68
+ Airbrake.notify(exception)
69
+
70
+ assert_sent notice, :exception => exception
71
+ end
72
+
73
+ should "create and send a notice for a hash" do
74
+ set_public_env
75
+ notice = stub_notice!
76
+ notice_args = { :error_message => 'uh oh' }
77
+ stub_sender!
78
+
79
+ Airbrake.notify(notice_args)
80
+
81
+ assert_sent(notice, notice_args)
82
+ end
83
+
84
+ should "not pass the hash as an exception when sending a notice for it" do
85
+ set_public_env
86
+ stub_notice!
87
+ notice_args = { :error_message => 'uh oh' }
88
+ stub_sender!
89
+
90
+ Airbrake.notify(notice_args)
91
+
92
+ assert_received(Airbrake::Notice, :new) {|expect| expect.with(Not(has_key(:exception))) }
93
+ end
94
+
95
+ should "create and send a notice for an exception that responds to to_hash" do
96
+ set_public_env
97
+ exception = build_exception
98
+ notice = stub_notice!
99
+ notice_args = { :error_message => 'uh oh' }
100
+ exception.stubs(:to_hash).returns(notice_args)
101
+ stub_sender!
102
+
103
+ Airbrake.notify(exception)
104
+
105
+ assert_sent(notice, notice_args.merge(:exception => exception))
106
+ end
107
+
108
+ should "create and sent a notice for an exception and hash" do
109
+ set_public_env
110
+ exception = build_exception
111
+ notice = stub_notice!
112
+ notice_args = { :error_message => 'uh oh' }
113
+ stub_sender!
114
+
115
+ Airbrake.notify(exception, notice_args)
116
+
117
+ assert_sent(notice, notice_args.merge(:exception => exception))
118
+ end
119
+
120
+ should "not create a notice in a development environment" do
121
+ set_development_env
122
+ sender = stub_sender!
123
+
124
+ Airbrake.notify(build_exception)
125
+ Airbrake.notify_or_ignore(build_exception)
126
+
127
+ assert_received(sender, :send_to_airbrake) {|expect| expect.never }
128
+ end
129
+
130
+ should "not deliver an ignored exception when notifying implicitly" do
131
+ set_public_env
132
+ exception = build_exception
133
+ sender = stub_sender!
134
+ notice = stub_notice!
135
+ notice.stubs(:ignore? => true)
136
+
137
+ Airbrake.notify_or_ignore(exception)
138
+
139
+ assert_received(sender, :send_to_airbrake) {|expect| expect.never }
140
+ end
141
+
142
+ should "deliver exception in async-mode" do
143
+ Airbrake.configure do |config|
144
+ config.environment_name = 'production'
145
+ config.async do |notice|
146
+ Airbrake.sender.send_to_airbrake(notice)
147
+ end
148
+ end
149
+ exception = build_exception
150
+ stub_sender!
151
+ notice = stub_notice!
152
+
153
+ Airbrake.notify(exception)
154
+
155
+ assert_sent(notice, :exception => exception)
156
+ end
157
+
158
+ should "pass notice in async-mode" do
159
+ received_notice = nil
160
+ Airbrake.configure do |config|
161
+ config.environment_name = 'production'
162
+ config.async {|notice| received_notice = notice}
163
+ end
164
+ exception = build_exception
165
+ stub_sender!
166
+ notice = stub_notice!
167
+
168
+ Airbrake.notify(exception)
169
+
170
+ assert_equal received_notice, notice
171
+ end
172
+
173
+ should "deliver an ignored exception when notifying manually" do
174
+ set_public_env
175
+ exception = build_exception
176
+ stub_sender!
177
+ notice = stub_notice!
178
+ notice.stubs(:ignore? => true)
179
+
180
+ Airbrake.notify(exception)
181
+
182
+ assert_sent(notice, :exception => exception)
183
+ end
184
+
185
+ should "pass config to created notices" do
186
+ exception = build_exception
187
+ config_opts = { 'one' => 'two', 'three' => 'four' }
188
+ stub_notice!
189
+ stub_sender!
190
+ Airbrake.configuration = stub('config', :merge => config_opts, :configured? => true, :public? => true,:async? => nil)
191
+
192
+ Airbrake.notify(exception)
193
+
194
+ assert_received(Airbrake::Notice, :new) do |expect|
195
+ expect.with(has_entries(config_opts))
196
+ end
197
+ end
198
+
199
+ context "building notice JSON for an exception" do
200
+ setup do
201
+ @params = { :controller => "users", :action => "create" }
202
+ @exception = build_exception
203
+ @hash = Airbrake.build_lookup_hash_for(@exception, @params)
204
+ end
205
+
206
+ should "set action" do
207
+ assert_equal @params[:action], @hash[:action]
208
+ end
209
+
210
+ should "set controller" do
211
+ assert_equal @params[:controller], @hash[:component]
212
+ end
213
+
214
+ should "set line number" do
215
+ assert @hash[:line_number] =~ /\d+/
216
+ end
217
+
218
+ should "set file" do
219
+ assert_match(/test\/helper\.rb$/, @hash[:file])
220
+ end
221
+
222
+ should "set rails_env to production" do
223
+ assert_equal 'production', @hash[:environment_name]
224
+ end
225
+
226
+ should "set error class" do
227
+ assert_equal @exception.class.to_s, @hash[:error_class]
228
+ end
229
+
230
+ should "not set file or line number with no backtrace" do
231
+ @exception.stubs(:backtrace).returns([])
232
+
233
+ @hash = Airbrake.build_lookup_hash_for(@exception)
234
+
235
+ assert_nil @hash[:line_number]
236
+ assert_nil @hash[:file]
237
+ end
238
+
239
+ should "not set action or controller when not provided" do
240
+ @hash = Airbrake.build_lookup_hash_for(@exception)
241
+
242
+ assert_nil @hash[:action]
243
+ assert_nil @hash[:controller]
244
+ end
245
+
246
+ context "when an exception that provides #original_exception is raised" do
247
+ setup do
248
+ @exception.stubs(:original_exception).returns(begin
249
+ raise NotifierTest::OriginalException.new
250
+ rescue Exception => e
251
+ e
252
+ end)
253
+ end
254
+
255
+ should "unwrap exceptions that provide #original_exception" do
256
+ @hash = Airbrake.build_lookup_hash_for(@exception)
257
+ assert_equal "NotifierTest::OriginalException", @hash[:error_class]
258
+ end
259
+
260
+ should "keep exception if #original_exception is nil" do
261
+ @exception.stubs(:original_exception).returns(nil)
262
+ @hash = Airbrake.build_lookup_hash_for(@exception)
263
+ assert_equal "BacktracedException", @hash[:error_class]
264
+ end
265
+ end
266
+
267
+ context "when an exception that provides #continued_exception is raised" do
268
+ setup do
269
+ @exception.stubs(:continued_exception).returns(begin
270
+ raise NotifierTest::ContinuedException.new
271
+ rescue Exception => e
272
+ e
273
+ end)
274
+ end
275
+
276
+ should "unwrap exceptions that provide #continued_exception" do
277
+ @hash = Airbrake.build_lookup_hash_for(@exception)
278
+ assert_equal "NotifierTest::ContinuedException", @hash[:error_class]
279
+ end
280
+
281
+ should "keep exception if #continued_exception is nil" do
282
+ @exception.stubs(:continued_exception).returns(nil)
283
+ @hash = Airbrake.build_lookup_hash_for(@exception)
284
+ assert_equal "BacktracedException", @hash[:error_class]
285
+ end
286
+ end
287
+ end
288
+ end
@@ -0,0 +1,204 @@
1
+ require File.expand_path '../helper', __FILE__
2
+
3
+ class ParamsCleanerTest < Test::Unit::TestCase
4
+
5
+ def clean(opts = {})
6
+ cleaner = Airbrake::Utils::ParamsCleaner.new(:blacklist_filters => opts.delete(:params_filters) || [],
7
+ :whitelist_filters => opts.delete(:whitelist_params_filters) || [],
8
+ :to_clean => opts)
9
+ cleaner.clean
10
+ end
11
+
12
+ def assert_serializes_hash(attribute)
13
+ [File.open(__FILE__), Proc.new { puts "boo!" }, Module.new, nil].each do |object|
14
+ hash = {
15
+ :strange_object => object,
16
+ :sub_hash => {
17
+ :sub_object => object
18
+ },
19
+ :array => [object]
20
+ }
21
+ clean_params = clean(attribute => hash)
22
+ hash = clean_params.send(attribute)
23
+ object_serialized = object.nil? ? nil : object.to_s
24
+ assert_equal object_serialized, hash[:strange_object], "objects should be serialized"
25
+ assert_kind_of Hash, hash[:sub_hash], "subhashes should be kept"
26
+ assert_equal object_serialized, hash[:sub_hash][:sub_object], "subhash members should be serialized"
27
+ assert_kind_of Array, hash[:array], "arrays should be kept"
28
+ assert_equal object_serialized, hash[:array].first, "array members should be serialized"
29
+ end
30
+ end
31
+
32
+ def assert_filters_hash(attribute)
33
+ filters = ['abc', :def]
34
+ original = {
35
+ 'abc' => '123',
36
+ 'def' => '456',
37
+ 'ghi' => '789',
38
+ 'something_with_abc' => 'match the entire string',
39
+ 'nested_hash' => { 'abc' => '100', 'ghi' => '789' },
40
+ 'nested_array' => [{ 'abc' => '100' }, { 'ghi' => '789' }, 'xyz', [['asd', []]]]
41
+ }
42
+ filtered = {
43
+ 'abc' => '[FILTERED]',
44
+ 'def' => '[FILTERED]',
45
+ 'ghi' => '789',
46
+ 'something_with_abc' => 'match the entire string',
47
+ 'nested_hash' => { 'abc' => '[FILTERED]', 'ghi' => '789' },
48
+ 'nested_array' => [{ 'abc' => '[FILTERED]' }, { 'ghi' => '789' }, 'xyz', [['asd', []]]]
49
+ }
50
+
51
+ clean_params = clean(:params_filters => filters, attribute => original)
52
+
53
+ assert_equal(filtered, clean_params.send(attribute))
54
+ end
55
+
56
+ should "should always remove a Rails application's secret token" do
57
+ original = {
58
+ "action_dispatch.secret_token" => "abc123xyz456",
59
+ "abc" => "123"
60
+ }
61
+ clean_params = clean(:cgi_data => original)
62
+ assert_equal({"abc" => "123"}, clean_params.cgi_data)
63
+ end
64
+
65
+ should "remove sensitive rack vars" do
66
+ original = {
67
+ "HTTP_X_CSRF_TOKEN" => "remove_me",
68
+ "HTTP_COOKIE" => "remove_me",
69
+ "HTTP_AUTHORIZATION" => "remove_me",
70
+ "action_dispatch.request.unsigned_session_cookie" => "remove_me",
71
+ "action_dispatch.cookies" => "remove_me",
72
+ "action_dispatch.unsigned_session_cookie" => "remove_me",
73
+ "action_dispatch.secret_key_base" => "remove_me",
74
+ "action_dispatch.signed_cookie_salt" => "remove_me",
75
+ "action_dispatch.encrypted_cookie_salt" => "remove_me",
76
+ "action_dispatch.encrypted_signed_cookie_salt" => "remove_me",
77
+ "action_dispatch.http_auth_salt" => "remove_me",
78
+ "action_dispatch.secret_token" => "remove_me",
79
+ "rack.request.cookie_hash" => "remove_me",
80
+ "rack.request.cookie_string" => "remove_me",
81
+ "rack.request.form_vars" => "remove_me",
82
+ "rack.session" => "remove_me",
83
+ "rack.session.options" => "remove_me",
84
+ "rack.request.form_vars" => "story%5Btitle%5D=The+TODO+label",
85
+ "abc" => "123"
86
+ }
87
+
88
+ clean_params = clean(:cgi_data => original)
89
+ assert_equal({"abc" => "123"}, clean_params.cgi_data)
90
+ end
91
+
92
+ should "remove secrets from cgi_data" do
93
+ original = {
94
+ "aws_secret_key" => "secret",
95
+ "service_password" => "password",
96
+ "abc" => "123"
97
+ }
98
+
99
+ clean_params = clean(:cgi_data => original)
100
+ assert_equal({"abc" => "123"}, clean_params.cgi_data)
101
+ end
102
+
103
+ should "handle frozen objects" do
104
+ params = {
105
+ 'filter_me' => ['a', 'b', 'c', 'd'].freeze
106
+ }
107
+
108
+ clean_params = clean({:params_filters => ['filter_me'], :parameters => params})
109
+ assert_equal({'filter_me' => '[FILTERED]'}, clean_params.parameters)
110
+ end
111
+
112
+ should "filter parameters" do
113
+ assert_filters_hash(:parameters)
114
+ end
115
+
116
+ should "whitelist filter parameters" do
117
+ whitelist_filters = ["abc", :def]
118
+ original = { 'abc' => "123", 'def' => "456", 'ghi' => "789", 'nested' => { 'abc' => '100' },
119
+ 'something_with_abc' => 'match the entire string'}
120
+ filtered = { 'abc' => "123",
121
+ 'def' => "456",
122
+ 'something_with_abc' => "[FILTERED]",
123
+ 'ghi' => "[FILTERED]",
124
+ 'nested' => "[FILTERED]" }
125
+
126
+ clean_params = clean(:whitelist_params_filters => whitelist_filters,
127
+ :parameters => original)
128
+
129
+ assert_equal(filtered,
130
+ clean_params.send(:parameters))
131
+ end
132
+
133
+ should "not filter everything if whitelist filters are empty" do
134
+ whitelist_filters = []
135
+ original = { 'abc' => '123' }
136
+ clean_params = clean(:whitelist_params_filters => whitelist_filters,
137
+ :parameters => original)
138
+ assert_equal(original, clean_params.send(:parameters))
139
+ end
140
+
141
+ should "not care if filters are defined in nested array" do
142
+ filters = [[/crazy/, :foo, ["bar", ["too"]]]]
143
+ original = {
144
+ 'this_is_crazy' => 'yes_it_is',
145
+ 'I_am_good' => 'yes_you_are',
146
+ 'foo' => '1212',
147
+ 'too' => '2121',
148
+ 'bar' => 'secret'
149
+ }
150
+ filtered = {
151
+ 'this_is_crazy' => '[FILTERED]',
152
+ 'I_am_good' => 'yes_you_are',
153
+ 'foo' => '[FILTERED]',
154
+ 'too' => '[FILTERED]',
155
+ 'bar' => '[FILTERED]'
156
+ }
157
+ clean_params = clean(:params_filters => filters,
158
+ :parameters => original)
159
+ assert_equal(filtered, clean_params.send(:parameters))
160
+ end
161
+
162
+ should "filter key if it is defined as blacklist and whitelist" do
163
+ original = { 'filter_me' => 'secret' }
164
+ filtered = { 'filter_me' => '[FILTERED]' }
165
+ clean_params = clean(:params_filters => [:filter_me],
166
+ :params_whitelist_filters => [:filter_me],
167
+ :parameters => original)
168
+ assert_equal(filtered, clean_params.send(:parameters))
169
+ end
170
+
171
+ should "filter cgi data" do
172
+ assert_filters_hash(:cgi_data)
173
+ end
174
+
175
+ should "filter session" do
176
+ assert_filters_hash(:session_data)
177
+ end
178
+
179
+ should "convert unserializable objects to strings" do
180
+ assert_serializes_hash(:parameters)
181
+ assert_serializes_hash(:cgi_data)
182
+ assert_serializes_hash(:session_data)
183
+ end
184
+
185
+ should "handle closed IO objects by converting them to strings" do
186
+ params = {
187
+ :files => [Tempfile.new('a').tap(&:close), IO.new(0).tap(&:close)]
188
+ }
189
+ clean_params = clean(:params_filters => ['files'], :parameters => params)
190
+ assert_match(/\A#<(Temp)?[Ff]ile:0x.+>\z/, clean_params.parameters[:files][0])
191
+ assert_match(/\A#<IO:0x.+>\z/, clean_params.parameters[:files][1])
192
+ end
193
+
194
+ should "not break on filtering multi-dimensional array as possible in action_dispatch.cookies" do
195
+ original = { 'cgi_cookies_to_filter' => [['any_cookie_key', 'some_cookie_value'], ['secret', 'some_secret_value']] }
196
+ clean_params = clean(:params_filters => [:secret],
197
+ :params_whitelist_filters => [:secret],
198
+ :parameters => original)
199
+ assert_nothing_raised do
200
+ clean_params.send(:parameters)
201
+ end
202
+ end
203
+
204
+ end