airbrake-ruby 3.2.2-java

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 (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