airbrake-ruby 3.2.2-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +7 -0
  2. data/lib/airbrake-ruby.rb +554 -0
  3. data/lib/airbrake-ruby/async_sender.rb +119 -0
  4. data/lib/airbrake-ruby/backtrace.rb +194 -0
  5. data/lib/airbrake-ruby/code_hunk.rb +53 -0
  6. data/lib/airbrake-ruby/config.rb +238 -0
  7. data/lib/airbrake-ruby/config/validator.rb +63 -0
  8. data/lib/airbrake-ruby/deploy_notifier.rb +47 -0
  9. data/lib/airbrake-ruby/file_cache.rb +48 -0
  10. data/lib/airbrake-ruby/filter_chain.rb +95 -0
  11. data/lib/airbrake-ruby/filters/context_filter.rb +29 -0
  12. data/lib/airbrake-ruby/filters/dependency_filter.rb +31 -0
  13. data/lib/airbrake-ruby/filters/exception_attributes_filter.rb +45 -0
  14. data/lib/airbrake-ruby/filters/gem_root_filter.rb +33 -0
  15. data/lib/airbrake-ruby/filters/git_last_checkout_filter.rb +90 -0
  16. data/lib/airbrake-ruby/filters/git_repository_filter.rb +42 -0
  17. data/lib/airbrake-ruby/filters/git_revision_filter.rb +66 -0
  18. data/lib/airbrake-ruby/filters/keys_blacklist.rb +50 -0
  19. data/lib/airbrake-ruby/filters/keys_filter.rb +140 -0
  20. data/lib/airbrake-ruby/filters/keys_whitelist.rb +49 -0
  21. data/lib/airbrake-ruby/filters/root_directory_filter.rb +28 -0
  22. data/lib/airbrake-ruby/filters/sql_filter.rb +104 -0
  23. data/lib/airbrake-ruby/filters/system_exit_filter.rb +23 -0
  24. data/lib/airbrake-ruby/filters/thread_filter.rb +92 -0
  25. data/lib/airbrake-ruby/hash_keyable.rb +37 -0
  26. data/lib/airbrake-ruby/ignorable.rb +44 -0
  27. data/lib/airbrake-ruby/nested_exception.rb +39 -0
  28. data/lib/airbrake-ruby/notice.rb +165 -0
  29. data/lib/airbrake-ruby/notice_notifier.rb +228 -0
  30. data/lib/airbrake-ruby/performance_notifier.rb +161 -0
  31. data/lib/airbrake-ruby/promise.rb +99 -0
  32. data/lib/airbrake-ruby/response.rb +71 -0
  33. data/lib/airbrake-ruby/stat.rb +56 -0
  34. data/lib/airbrake-ruby/sync_sender.rb +111 -0
  35. data/lib/airbrake-ruby/tdigest.rb +393 -0
  36. data/lib/airbrake-ruby/time_truncate.rb +17 -0
  37. data/lib/airbrake-ruby/truncator.rb +115 -0
  38. data/lib/airbrake-ruby/version.rb +6 -0
  39. data/spec/airbrake_spec.rb +171 -0
  40. data/spec/async_sender_spec.rb +154 -0
  41. data/spec/backtrace_spec.rb +438 -0
  42. data/spec/code_hunk_spec.rb +118 -0
  43. data/spec/config/validator_spec.rb +189 -0
  44. data/spec/config_spec.rb +281 -0
  45. data/spec/deploy_notifier_spec.rb +41 -0
  46. data/spec/file_cache.rb +36 -0
  47. data/spec/filter_chain_spec.rb +83 -0
  48. data/spec/filters/context_filter_spec.rb +25 -0
  49. data/spec/filters/dependency_filter_spec.rb +14 -0
  50. data/spec/filters/exception_attributes_filter_spec.rb +63 -0
  51. data/spec/filters/gem_root_filter_spec.rb +44 -0
  52. data/spec/filters/git_last_checkout_filter_spec.rb +48 -0
  53. data/spec/filters/git_repository_filter.rb +53 -0
  54. data/spec/filters/git_revision_filter_spec.rb +126 -0
  55. data/spec/filters/keys_blacklist_spec.rb +236 -0
  56. data/spec/filters/keys_whitelist_spec.rb +205 -0
  57. data/spec/filters/root_directory_filter_spec.rb +42 -0
  58. data/spec/filters/sql_filter_spec.rb +219 -0
  59. data/spec/filters/system_exit_filter_spec.rb +14 -0
  60. data/spec/filters/thread_filter_spec.rb +279 -0
  61. data/spec/fixtures/notroot.txt +7 -0
  62. data/spec/fixtures/project_root/code.rb +221 -0
  63. data/spec/fixtures/project_root/empty_file.rb +0 -0
  64. data/spec/fixtures/project_root/long_line.txt +1 -0
  65. data/spec/fixtures/project_root/short_file.rb +3 -0
  66. data/spec/fixtures/project_root/vendor/bundle/ignored_file.rb +5 -0
  67. data/spec/helpers.rb +9 -0
  68. data/spec/ignorable_spec.rb +14 -0
  69. data/spec/nested_exception_spec.rb +75 -0
  70. data/spec/notice_notifier_spec.rb +436 -0
  71. data/spec/notice_notifier_spec/options_spec.rb +266 -0
  72. data/spec/notice_spec.rb +297 -0
  73. data/spec/performance_notifier_spec.rb +287 -0
  74. data/spec/promise_spec.rb +165 -0
  75. data/spec/response_spec.rb +82 -0
  76. data/spec/spec_helper.rb +102 -0
  77. data/spec/stat_spec.rb +35 -0
  78. data/spec/sync_sender_spec.rb +140 -0
  79. data/spec/tdigest_spec.rb +230 -0
  80. data/spec/time_truncate_spec.rb +13 -0
  81. data/spec/truncator_spec.rb +238 -0
  82. metadata +278 -0
@@ -0,0 +1,236 @@
1
+ RSpec.describe Airbrake::Filters::KeysBlacklist do
2
+ subject { described_class.new(Logger.new('/dev/null'), patterns) }
3
+
4
+ let(:notice) do
5
+ Airbrake::Notice.new(Airbrake::Config.new, AirbrakeTestError.new)
6
+ end
7
+
8
+ shared_examples 'pattern matching' do |patts, params|
9
+ let(:patterns) { patts }
10
+
11
+ it "filters out the matching values" do
12
+ notice[:params] = params.first
13
+ subject.call(notice)
14
+ expect(notice[:params]).to eq(params.last)
15
+ end
16
+ end
17
+
18
+ context "when a pattern is a Regexp" do
19
+ include_examples(
20
+ 'pattern matching',
21
+ [/\Abon/],
22
+ [
23
+ { bongo: 'bango' },
24
+ { bongo: '[Filtered]' }
25
+ ]
26
+ )
27
+
28
+ context "and when a key is a hash" do
29
+ let(:patterns) { [/bango/] }
30
+
31
+ # https://github.com/airbrake/airbrake/issues/739
32
+ it "doesn't fail" do
33
+ notice[:params] = { bingo: { {} => 'unfiltered' } }
34
+ expect { subject.call(notice) }.not_to raise_error
35
+ end
36
+ end
37
+ end
38
+
39
+ context "when a pattern is a Symbol" do
40
+ include_examples(
41
+ 'pattern matching',
42
+ [:bingo],
43
+ [
44
+ { bingo: 'bango' },
45
+ { bingo: '[Filtered]' }
46
+ ]
47
+ )
48
+ end
49
+
50
+ context "when a pattern is a String" do
51
+ include_examples(
52
+ 'pattern matching',
53
+ ['bingo'],
54
+ [
55
+ { bingo: 'bango' },
56
+ { bingo: '[Filtered]' }
57
+ ]
58
+ )
59
+ end
60
+
61
+ context "when a pattern is a Array of Hash" do
62
+ include_examples(
63
+ 'pattern matching',
64
+ ['bingo'],
65
+ [
66
+ { array: [{ bingo: 'bango' }, []] },
67
+ { array: [{ bingo: '[Filtered]' }, []] }
68
+ ]
69
+ )
70
+ end
71
+
72
+ context "when a Proc pattern was provided" do
73
+ context "along with normal keys" do
74
+ include_examples(
75
+ 'pattern matching',
76
+ [proc { 'bongo' }, :bash],
77
+ [
78
+ { bingo: 'bango', bongo: 'bish', bash: 'bosh' },
79
+ { bingo: 'bango', bongo: '[Filtered]', bash: '[Filtered]' }
80
+ ]
81
+ )
82
+ end
83
+
84
+ context "which doesn't return an array of keys" do
85
+ include_examples(
86
+ 'pattern matching',
87
+ [proc { Object.new }],
88
+ [
89
+ { bingo: 'bango', bongo: 'bish' },
90
+ { bingo: 'bango', bongo: 'bish' }
91
+ ]
92
+ )
93
+
94
+ it "logs an error" do
95
+ out = StringIO.new
96
+ logger = Logger.new(out)
97
+ keys_blacklist = described_class.new(logger, patterns)
98
+ keys_blacklist.call(notice)
99
+
100
+ expect(out.string).to(
101
+ match(/ERROR.+KeysBlacklist is invalid.+patterns: \[#<Object:.+>\]/)
102
+ )
103
+ end
104
+ end
105
+
106
+ context "which returns another Proc" do
107
+ let(:patterns) { [proc { proc { ['bingo'] } }] }
108
+
109
+ context "and when the filter is called once" do
110
+ it "logs an error" do
111
+ out = StringIO.new
112
+ logger = Logger.new(out)
113
+ keys_blacklist = described_class.new(logger, patterns)
114
+ keys_blacklist.call(notice)
115
+
116
+ expect(out.string).to(
117
+ match(/ERROR.+KeysBlacklist is invalid.+patterns: \[#<Proc:.+>\]/)
118
+ )
119
+ end
120
+ end
121
+
122
+ context "and when the filter is called twice" do
123
+ it "unwinds procs and filters keys" do
124
+ notice[:params] = { bingo: 'bango', bongo: 'bish' }
125
+ 2.times { subject.call(notice) }
126
+ expect(notice[:params]).to eq(bingo: '[Filtered]', bongo: 'bish')
127
+ end
128
+ end
129
+ end
130
+ end
131
+
132
+ context "when a pattern is invalid" do
133
+ include_examples(
134
+ 'pattern matching',
135
+ [Object.new],
136
+ [
137
+ { bingo: 'bango', bongo: 'bish' },
138
+ { bingo: 'bango', bongo: 'bish' }
139
+ ]
140
+ )
141
+
142
+ it "logs an error" do
143
+ out = StringIO.new
144
+ logger = Logger.new(out)
145
+ keys_blacklist = described_class.new(logger, patterns)
146
+ keys_blacklist.call(notice)
147
+
148
+ expect(out.string).to(
149
+ match(/ERROR.+KeysBlacklist is invalid.+patterns: \[#<Object:.+>\]/)
150
+ )
151
+ end
152
+ end
153
+
154
+ context "when a value contains a nested hash" do
155
+ context "and it is non-recursive" do
156
+ include_examples(
157
+ 'pattern matching',
158
+ ['bish'],
159
+ [
160
+ { bongo: { bish: 'bash' } },
161
+ { bongo: { bish: '[Filtered]' } }
162
+ ]
163
+ )
164
+ end
165
+
166
+ context "and it is recursive" do
167
+ bongo = { bingo: {} }
168
+ bongo[:bingo][:bango] = bongo
169
+
170
+ include_examples(
171
+ 'pattern matching',
172
+ ['bango'],
173
+ [
174
+ bongo,
175
+ { bingo: { bango: '[Filtered]' } }
176
+ ]
177
+ )
178
+ end
179
+ end
180
+
181
+ describe "context payload" do
182
+ context "when a URL with query params is present" do
183
+ let(:patterns) { ['bish'] }
184
+
185
+ it "filters the params" do
186
+ notice[:context][:url] =
187
+ 'http://localhost:3000/crash?foo=bar&baz=bongo&bish=bash&color=%23FFAAFF'
188
+
189
+ subject.call(notice)
190
+ expect(notice[:context][:url]).to(
191
+ eq 'http://localhost:3000/crash?foo=bar&baz=bongo&bish=[Filtered]&color=%23FFAAFF'
192
+ )
193
+ end
194
+ end
195
+
196
+ context "when the user key is present" do
197
+ let(:patterns) { ['user'] }
198
+
199
+ it "filters out the user" do
200
+ notice[:context][:user] = { id: 1337, name: 'Bingo Bango' }
201
+ subject.call(notice)
202
+ expect(notice[:context][:user]).to eq('[Filtered]')
203
+ end
204
+ end
205
+
206
+ context "and when it is a hash" do
207
+ let(:patterns) { ['name'] }
208
+
209
+ it "filters out individual user fields" do
210
+ notice[:context][:user] = { id: 1337, name: 'Bingo Bango' }
211
+ subject.call(notice)
212
+ expect(notice[:context][:user][:name]).to eq('[Filtered]')
213
+ end
214
+ end
215
+ end
216
+
217
+ context "when the headers key is present" do
218
+ let(:patterns) { ['headers'] }
219
+
220
+ it "filters out the headers" do
221
+ notice[:context][:headers] = { 'HTTP_COOKIE' => 'banana' }
222
+ subject.call(notice)
223
+ expect(notice[:context][:headers]).to eq('[Filtered]')
224
+ end
225
+
226
+ context "and when it is a hash" do
227
+ let(:patterns) { ['HTTP_COOKIE'] }
228
+
229
+ it "filters out individual header fields" do
230
+ notice[:context][:headers] = { 'HTTP_COOKIE' => 'banana' }
231
+ subject.call(notice)
232
+ expect(notice[:context][:headers]['HTTP_COOKIE']).to eq('[Filtered]')
233
+ end
234
+ end
235
+ end
236
+ end
@@ -0,0 +1,205 @@
1
+ RSpec.describe Airbrake::Filters::KeysWhitelist do
2
+ subject { described_class.new(Logger.new('/dev/null'), patterns) }
3
+
4
+ let(:notice) do
5
+ Airbrake::Notice.new(Airbrake::Config.new, AirbrakeTestError.new)
6
+ end
7
+
8
+ shared_examples 'pattern matching' do |patts, params|
9
+ let(:patterns) { patts }
10
+
11
+ it "filters out the matching values" do
12
+ notice[:params] = params.first
13
+ subject.call(notice)
14
+ expect(notice[:params]).to eq(params.last)
15
+ end
16
+ end
17
+
18
+ context "when a pattern is a Regexp" do
19
+ include_examples(
20
+ 'pattern matching',
21
+ [/\Abin/],
22
+ [
23
+ { bingo: 'bango', bongo: 'bish', bash: 'bosh' },
24
+ { bingo: 'bango', bongo: '[Filtered]', bash: '[Filtered]' }
25
+ ]
26
+ )
27
+ end
28
+
29
+ context "when a pattern is a Symbol" do
30
+ include_examples(
31
+ 'pattern matching',
32
+ [:bongo],
33
+ [
34
+ { bongo: 'bish', bash: 'bosh', bbashh: 'bboshh' },
35
+ { bongo: 'bish', bash: '[Filtered]', bbashh: '[Filtered]' }
36
+ ]
37
+ )
38
+ end
39
+
40
+ context "when a pattern is a String" do
41
+ include_examples(
42
+ 'pattern matching',
43
+ ['bash'],
44
+ [
45
+ { bingo: 'bango', bongo: 'bish', bash: 'bosh' },
46
+ { bingo: '[Filtered]', bongo: '[Filtered]', bash: 'bosh' }
47
+ ]
48
+ )
49
+ end
50
+
51
+ context "when a Proc pattern was provided" do
52
+ context "along with normal keys" do
53
+ include_examples(
54
+ 'pattern matching',
55
+ [proc { 'bongo' }, :bash],
56
+ [
57
+ { bingo: 'bango', bongo: 'bish', bash: 'bosh' },
58
+ { bingo: '[Filtered]', bongo: 'bish', bash: 'bosh' }
59
+ ]
60
+ )
61
+ end
62
+
63
+ context "which doesn't return an array of keys" do
64
+ include_examples(
65
+ 'pattern matching',
66
+ [proc { Object.new }],
67
+ [
68
+ { bingo: 'bango', bongo: 'bish', bash: 'bosh' },
69
+ { bingo: '[Filtered]', bongo: '[Filtered]', bash: '[Filtered]' }
70
+ ]
71
+ )
72
+
73
+ it "logs an error" do
74
+ out = StringIO.new
75
+ logger = Logger.new(out)
76
+ keys_whitelist = described_class.new(logger, patterns)
77
+ keys_whitelist.call(notice)
78
+
79
+ expect(out.string).to(
80
+ match(/ERROR.+KeysWhitelist is invalid.+patterns: \[#<Object:.+>\]/)
81
+ )
82
+ end
83
+ end
84
+
85
+ context "which returns another Proc" do
86
+ let(:patterns) { [proc { proc { ['bingo'] } }] }
87
+
88
+ context "and when the filter is called once" do
89
+ it "logs an error" do
90
+ out = StringIO.new
91
+ logger = Logger.new(out)
92
+ keys_whitelist = described_class.new(logger, patterns)
93
+ keys_whitelist.call(notice)
94
+
95
+ expect(out.string).to(
96
+ match(/ERROR.+KeysWhitelist is invalid.+patterns: \[#<Proc:.+>\]/)
97
+ )
98
+ end
99
+
100
+ include_examples(
101
+ 'pattern matching',
102
+ [proc { proc { ['bingo'] } }],
103
+ [
104
+ { bingo: 'bango', bongo: 'bish', bash: 'bosh' },
105
+ { bingo: '[Filtered]', bongo: '[Filtered]', bash: '[Filtered]' }
106
+ ]
107
+ )
108
+ end
109
+ end
110
+ end
111
+
112
+ context "when a pattern is invalid" do
113
+ include_examples(
114
+ 'pattern matching',
115
+ [Object.new],
116
+ [
117
+ { bingo: 'bango', bongo: 'bish', bash: 'bosh' },
118
+ { bingo: '[Filtered]', bongo: '[Filtered]', bash: '[Filtered]' }
119
+ ]
120
+ )
121
+
122
+ it "logs an error" do
123
+ out = StringIO.new
124
+ logger = Logger.new(out)
125
+ keys_whitelist = described_class.new(logger, patterns)
126
+ keys_whitelist.call(notice)
127
+
128
+ expect(out.string).to(
129
+ match(/ERROR.+KeysWhitelist is invalid.+patterns: \[#<Object:.+>\]/)
130
+ )
131
+ end
132
+ end
133
+
134
+ context "when a value contains a nested hash" do
135
+ context "and it is non-recursive" do
136
+ include_examples(
137
+ 'pattern matching',
138
+ %w[bongo bish],
139
+ [
140
+ { bingo: 'bango', bongo: { bish: 'bash' } },
141
+ { bingo: '[Filtered]', bongo: { bish: 'bash' } }
142
+ ]
143
+ )
144
+ end
145
+
146
+ context "and it is recursive" do
147
+ let(:patterns) { ['bingo'] }
148
+
149
+ it "raises error (MRI)", skip: (
150
+ # MRI 2.3 & 2.4 may segfault on Circle CI. Example build:
151
+ # https://circleci.com/workflow-run/c112358c-e7bf-4789-9eb2-4891ea84da68
152
+ RUBY_ENGINE == 'ruby' && RUBY_VERSION =~ /\A2\.[34]\.\d+\z/
153
+ ) do
154
+ bongo = {}
155
+ bongo[:bingo] = bongo
156
+ notice[:params] = bongo
157
+
158
+ begin
159
+ expect { subject.call(notice) }.to raise_error(SystemStackError)
160
+ rescue RSpec::Expectations::ExpectationNotMetError => ex
161
+ # JRuby might raise two different exceptions, which represent the same
162
+ # thing. One is a Java exception, the other is a Ruby exception.
163
+ # Likely a bug: https://github.com/jruby/jruby/issues/1903
164
+ raise ex unless RUBY_ENGINE == 'jruby'
165
+ expect { subject.call(notice) }.to raise_error(java.lang.StackOverflowError)
166
+ end
167
+ end
168
+ end
169
+ end
170
+
171
+ describe "context payload" do
172
+ describe "URL" do
173
+ let(:patterns) { ['bish'] }
174
+
175
+ context "when it contains query params" do
176
+ it "filters the params" do
177
+ notice[:context][:url] = 'http://localhost:3000/crash?foo=bar&baz=bongo&bish=bash'
178
+ subject.call(notice)
179
+ expect(notice[:context][:url]).to(
180
+ eq('http://localhost:3000/crash?foo=[Filtered]&baz=[Filtered]&bish=bash')
181
+ )
182
+ end
183
+ end
184
+
185
+ context "when it is invalid" do
186
+ it "leaves the URL unfiltered" do
187
+ notice[:context][:url] =
188
+ 'http://localhost:3000/cra]]]sh?foo=bar&baz=bongo&bish=bash'
189
+ subject.call(notice)
190
+ expect(notice[:context][:url]).to(
191
+ eq('http://localhost:3000/cra]]]sh?foo=bar&baz=bongo&bish=bash')
192
+ )
193
+ end
194
+ end
195
+
196
+ context "when it is without a query" do
197
+ it "leaves the URL untouched" do
198
+ notice[:context][:url] = 'http://localhost:3000/crash'
199
+ subject.call(notice)
200
+ expect(notice[:context][:url]).to(eq('http://localhost:3000/crash'))
201
+ end
202
+ end
203
+ end
204
+ end
205
+ end
@@ -0,0 +1,42 @@
1
+ RSpec.describe Airbrake::Filters::RootDirectoryFilter do
2
+ subject { described_class.new(root_directory) }
3
+
4
+ let(:root_directory) { '/var/www/project' }
5
+
6
+ let(:notice) do
7
+ Airbrake::Notice.new(Airbrake::Config.new, AirbrakeTestError.new)
8
+ end
9
+
10
+ it "replaces root directory in the backtrace with a label" do
11
+ # rubocop:disable Metrics/LineLength
12
+ notice[:errors].first[:backtrace] = [
13
+ { file: "/home/kyrylo/code/airbrake/ruby/spec/spec_helper.rb" },
14
+ { file: "#{root_directory}/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb " },
15
+ { file: "/opt/rubies/ruby-2.2.2/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb" },
16
+ { file: "#{root_directory}/gems/rspec-core-3.3.2/exe/rspec" }
17
+ ]
18
+ # rubocop:enable Metrics/LineLength
19
+
20
+ subject.call(notice)
21
+
22
+ # rubocop:disable Metrics/LineLength
23
+ expect(notice[:errors].first[:backtrace]).to(
24
+ eq(
25
+ [
26
+ { file: "/home/kyrylo/code/airbrake/ruby/spec/spec_helper.rb" },
27
+ { file: "/PROJECT_ROOT/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb " },
28
+ { file: "/opt/rubies/ruby-2.2.2/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb" },
29
+ { file: "/PROJECT_ROOT/gems/rspec-core-3.3.2/exe/rspec" }
30
+ ]
31
+ )
32
+ )
33
+ # rubocop:enable Metrics/LineLength
34
+ end
35
+
36
+ it "does not filter file when it is nil" do
37
+ expect(notice[:errors].first[:file]).to be_nil
38
+ expect { subject.call(notice) }.not_to(
39
+ change { notice[:errors].first[:file] }
40
+ )
41
+ end
42
+ end