airbrake-ruby 2.2.3 → 2.2.4

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.
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Airbrake::Filters::GemRootFilter do
4
+ let(:notice) do
5
+ Airbrake::Notice.new(Airbrake::Config.new, AirbrakeTestError.new)
6
+ end
7
+
8
+ let(:root1) { '/my/gem/root' }
9
+ let(:root2) { '/my/other/gem/root' }
10
+
11
+ before { Gem.path << root1 << root2 }
12
+ after { 2.times { Gem.path.pop } }
13
+
14
+ it "replaces gem root in the backtrace with a label" do
15
+ # rubocop:disable Metrics/LineLength
16
+ notice[:errors].first[:backtrace] = [
17
+ { file: "/home/kyrylo/code/airbrake/ruby/spec/spec_helper.rb" },
18
+ { file: "#{root1}/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb" },
19
+ { file: "/opt/rubies/ruby-2.2.2/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb" },
20
+ { file: "#{root2}/gems/rspec-core-3.3.2/exe/rspec" }
21
+ ]
22
+ # rubocop:enable Metrics/LineLength
23
+
24
+ subject.call(notice)
25
+
26
+ # rubocop:disable Metrics/LineLength
27
+ expect(notice[:errors].first[:backtrace]).to(
28
+ eq(
29
+ [
30
+ { file: "/home/kyrylo/code/airbrake/ruby/spec/spec_helper.rb" },
31
+ { file: "[GEM_ROOT]/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb" },
32
+ { file: "/opt/rubies/ruby-2.2.2/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb" },
33
+ { file: "[GEM_ROOT]/gems/rspec-core-3.3.2/exe/rspec" }
34
+ ]
35
+ )
36
+ )
37
+ # rubocop:enable Metrics/LineLength
38
+ end
39
+
40
+ it "does not filter file when it is nil" do
41
+ expect(notice[:errors].first[:file]).to be_nil
42
+ expect { subject.call(notice) }.not_to(
43
+ change { notice[:errors].first[:file] }
44
+ )
45
+ end
46
+ end
@@ -1,22 +1,197 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  RSpec.describe Airbrake::Filters::KeysBlacklist do
4
- subject do
5
- described_class.new(Logger.new('/dev/null'), patterns)
4
+ subject { described_class.new(Logger.new('/dev/null'), patterns) }
5
+
6
+ let(:notice) do
7
+ Airbrake::Notice.new(Airbrake::Config.new, AirbrakeTestError.new)
6
8
  end
7
9
 
8
- describe "#call" do
9
- let(:notice) do
10
- Airbrake::Notice.new(Airbrake::Config.new, AirbrakeTestError.new)
10
+ shared_examples 'pattern matching' do |patts, params|
11
+ let(:patterns) { patts }
12
+
13
+ it "filters out the matching values" do
14
+ notice[:params] = params.first
15
+ subject.call(notice)
16
+ expect(notice[:params]).to eq(params.last)
11
17
  end
18
+ end
12
19
 
13
- context "when a pattern is a regexp and when a key is a hash" do
20
+ context "when a pattern is a Regexp" do
21
+ include_examples(
22
+ 'pattern matching',
23
+ [/\Abon/],
24
+ [
25
+ { bongo: 'bango' },
26
+ { bongo: '[Filtered]' }
27
+ ]
28
+ )
29
+
30
+ context "and when a key is a hash" do
14
31
  let(:patterns) { [/bango/] }
15
32
 
33
+ # https://github.com/airbrake/airbrake/issues/739
16
34
  it "doesn't fail" do
17
35
  notice[:params] = { bingo: { {} => 'unfiltered' } }
18
36
  expect { subject.call(notice) }.not_to raise_error
19
37
  end
20
38
  end
21
39
  end
40
+
41
+ context "when a pattern is a Symbol" do
42
+ include_examples(
43
+ 'pattern matching',
44
+ [:bingo],
45
+ [
46
+ { bingo: 'bango' },
47
+ { bingo: '[Filtered]' }
48
+ ]
49
+ )
50
+ end
51
+
52
+ context "when a pattern is a String" do
53
+ include_examples(
54
+ 'pattern matching',
55
+ ['bingo'],
56
+ [
57
+ { bingo: 'bango' },
58
+ { bingo: '[Filtered]' }
59
+ ]
60
+ )
61
+ end
62
+
63
+ context "when a Proc pattern was provided" do
64
+ context "along with normal keys" do
65
+ include_examples(
66
+ 'pattern matching',
67
+ [proc { 'bongo' }, :bash],
68
+ [
69
+ { bingo: 'bango', bongo: 'bish', bash: 'bosh' },
70
+ { bingo: 'bango', bongo: '[Filtered]', bash: '[Filtered]' }
71
+ ]
72
+ )
73
+ end
74
+
75
+ context "which doesn't return an array of keys" do
76
+ include_examples(
77
+ 'pattern matching',
78
+ [proc { Object.new }],
79
+ [
80
+ { bingo: 'bango', bongo: 'bish' },
81
+ { bingo: 'bango', bongo: 'bish' }
82
+ ]
83
+ )
84
+
85
+ it "logs an error" do
86
+ out = StringIO.new
87
+ logger = Logger.new(out)
88
+ keys_blacklist = described_class.new(logger, patterns)
89
+ keys_blacklist.call(notice)
90
+
91
+ expect(out.string).to(
92
+ match(/ERROR.+KeysBlacklist is invalid.+patterns: \[#<Object:.+>\]/)
93
+ )
94
+ end
95
+ end
96
+
97
+ context "which returns another Proc" do
98
+ let(:patterns) { [proc { proc { ['bingo'] } }] }
99
+
100
+ context "and when the filter is called once" do
101
+ it "logs an error" do
102
+ out = StringIO.new
103
+ logger = Logger.new(out)
104
+ keys_blacklist = described_class.new(logger, patterns)
105
+ keys_blacklist.call(notice)
106
+
107
+ expect(out.string).to(
108
+ match(/ERROR.+KeysBlacklist is invalid.+patterns: \[#<Proc:.+>\]/)
109
+ )
110
+ end
111
+ end
112
+
113
+ context "and when the filter is called twice" do
114
+ it "unwinds procs and filters keys" do
115
+ notice[:params] = { bingo: 'bango', bongo: 'bish' }
116
+ 2.times { subject.call(notice) }
117
+ expect(notice[:params]).to eq(bingo: '[Filtered]', bongo: 'bish')
118
+ end
119
+ end
120
+ end
121
+ end
122
+
123
+ context "when a pattern is invalid" do
124
+ include_examples(
125
+ 'pattern matching',
126
+ [Object.new],
127
+ [
128
+ { bingo: 'bango', bongo: 'bish' },
129
+ { bingo: 'bango', bongo: 'bish' }
130
+ ]
131
+ )
132
+
133
+ it "logs an error" do
134
+ out = StringIO.new
135
+ logger = Logger.new(out)
136
+ keys_blacklist = described_class.new(logger, patterns)
137
+ keys_blacklist.call(notice)
138
+
139
+ expect(out.string).to(
140
+ match(/ERROR.+KeysBlacklist is invalid.+patterns: \[#<Object:.+>\]/)
141
+ )
142
+ end
143
+ end
144
+
145
+ context "when a value contains a nested hash" do
146
+ context "and it is non-recursive" do
147
+ include_examples(
148
+ 'pattern matching',
149
+ ['bish'],
150
+ [
151
+ { bongo: { bish: 'bash' } },
152
+ { bongo: { bish: '[Filtered]' } }
153
+ ]
154
+ )
155
+ end
156
+
157
+ context "and it is recursive" do
158
+ bongo = { bingo: {} }
159
+ bongo[:bingo][:bango] = bongo
160
+
161
+ include_examples(
162
+ 'pattern matching',
163
+ ['bango'],
164
+ [
165
+ bongo,
166
+ { bingo: { bango: '[Filtered]' } }
167
+ ]
168
+ )
169
+ end
170
+ end
171
+
172
+ describe "context payload" do
173
+ context "when a URL with query params is present" do
174
+ let(:patterns) { ['bish'] }
175
+
176
+ it "filters the params" do
177
+ notice[:context][:url] =
178
+ 'http://localhost:3000/crash?foo=bar&baz=bongo&bish=bash&color=%23FFAAFF'
179
+
180
+ subject.call(notice)
181
+ expect(notice[:context][:url]).to(
182
+ eq 'http://localhost:3000/crash?foo=bar&baz=bongo&bish=[Filtered]&color=%23FFAAFF'
183
+ )
184
+ end
185
+ end
186
+
187
+ context "when the user key is present" do
188
+ let(:patterns) { ['user'] }
189
+
190
+ it "filters out the user" do
191
+ notice[:context][:user] = { id: 1337, name: 'Bingo Bango' }
192
+ subject.call(notice)
193
+ expect(notice[:context][:user]).to eq('[Filtered]')
194
+ end
195
+ end
196
+ end
22
197
  end
@@ -0,0 +1,213 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Airbrake::Filters::KeysWhitelist do
4
+ subject { described_class.new(Logger.new('/dev/null'), patterns) }
5
+
6
+ let(:notice) do
7
+ Airbrake::Notice.new(Airbrake::Config.new, AirbrakeTestError.new)
8
+ end
9
+
10
+ shared_examples 'pattern matching' do |patts, params|
11
+ let(:patterns) { patts }
12
+
13
+ it "filters out the matching values" do
14
+ notice[:params] = params.first
15
+ subject.call(notice)
16
+ expect(notice[:params]).to eq(params.last)
17
+ end
18
+ end
19
+
20
+ context "when a pattern is a Regexp" do
21
+ include_examples(
22
+ 'pattern matching',
23
+ [/\Abin/],
24
+ [
25
+ { bingo: 'bango', bongo: 'bish', bash: 'bosh' },
26
+ { bingo: 'bango', bongo: '[Filtered]', bash: '[Filtered]' }
27
+ ]
28
+ )
29
+ end
30
+
31
+ context "when a pattern is a Symbol" do
32
+ include_examples(
33
+ 'pattern matching',
34
+ [:bongo],
35
+ [
36
+ { bongo: 'bish', bash: 'bosh', bbashh: 'bboshh' },
37
+ { bongo: 'bish', bash: '[Filtered]', bbashh: '[Filtered]' }
38
+ ]
39
+ )
40
+ end
41
+
42
+ context "when a pattern is a String" do
43
+ include_examples(
44
+ 'pattern matching',
45
+ ['bash'],
46
+ [
47
+ { bingo: 'bango', bongo: 'bish', bash: 'bosh' },
48
+ { bingo: '[Filtered]', bongo: '[Filtered]', bash: 'bosh' }
49
+ ]
50
+ )
51
+ end
52
+
53
+ context "when a Proc pattern was provided" do
54
+ context "along with normal keys" do
55
+ include_examples(
56
+ 'pattern matching',
57
+ [proc { 'bongo' }, :bash],
58
+ [
59
+ { bingo: 'bango', bongo: 'bish', bash: 'bosh' },
60
+ { bingo: '[Filtered]', bongo: 'bish', bash: 'bosh' }
61
+ ]
62
+ )
63
+ end
64
+
65
+ context "which doesn't return an array of keys" do
66
+ include_examples(
67
+ 'pattern matching',
68
+ [proc { Object.new }],
69
+ [
70
+ { bingo: 'bango', bongo: 'bish', bash: 'bosh' },
71
+ { bingo: '[Filtered]', bongo: '[Filtered]', bash: '[Filtered]' }
72
+ ]
73
+ )
74
+
75
+ it "logs an error" do
76
+ out = StringIO.new
77
+ logger = Logger.new(out)
78
+ keys_whitelist = described_class.new(logger, patterns)
79
+ keys_whitelist.call(notice)
80
+
81
+ expect(out.string).to(
82
+ match(/ERROR.+KeysWhitelist is invalid.+patterns: \[#<Object:.+>\]/)
83
+ )
84
+ end
85
+ end
86
+
87
+ context "which returns another Proc" do
88
+ let(:patterns) { [proc { proc { ['bingo'] } }] }
89
+
90
+ context "and when the filter is called once" do
91
+ it "logs an error" do
92
+ out = StringIO.new
93
+ logger = Logger.new(out)
94
+ keys_whitelist = described_class.new(logger, patterns)
95
+ keys_whitelist.call(notice)
96
+
97
+ expect(out.string).to(
98
+ match(/ERROR.+KeysWhitelist is invalid.+patterns: \[#<Proc:.+>\]/)
99
+ )
100
+ end
101
+
102
+ include_examples(
103
+ 'pattern matching',
104
+ [proc { proc { ['bingo'] } }],
105
+ [
106
+ { bingo: 'bango', bongo: 'bish', bash: 'bosh' },
107
+ { bingo: '[Filtered]', bongo: '[Filtered]', bash: '[Filtered]' }
108
+ ]
109
+ )
110
+ end
111
+ end
112
+ end
113
+
114
+ context "when a pattern is invalid" do
115
+ include_examples(
116
+ 'pattern matching',
117
+ [Object.new],
118
+ [
119
+ { bingo: 'bango', bongo: 'bish', bash: 'bosh' },
120
+ { bingo: '[Filtered]', bongo: '[Filtered]', bash: '[Filtered]' }
121
+ ]
122
+ )
123
+
124
+ it "logs an error" do
125
+ out = StringIO.new
126
+ logger = Logger.new(out)
127
+ keys_whitelist = described_class.new(logger, patterns)
128
+ keys_whitelist.call(notice)
129
+
130
+ expect(out.string).to(
131
+ match(/ERROR.+KeysWhitelist is invalid.+patterns: \[#<Object:.+>\]/)
132
+ )
133
+ end
134
+ end
135
+
136
+ context "when a value contains a nested hash" do
137
+ context "and it is non-recursive" do
138
+ include_examples(
139
+ 'pattern matching',
140
+ %w[bongo bish],
141
+ [
142
+ { bingo: 'bango', bongo: { bish: 'bash' } },
143
+ { bingo: '[Filtered]', bongo: { bish: 'bash' } }
144
+ ]
145
+ )
146
+ end
147
+
148
+ context "and it is recursive" do
149
+ let(:patterns) { %w[bingo bango] }
150
+
151
+ it "errors when nested hashes are not filtered" do
152
+ bongo = { bingo: {} }
153
+ bongo[:bingo][:bango] = bongo
154
+ notice[:params] = bongo
155
+
156
+ if RUBY_ENGINE == 'jruby'
157
+ # JRuby might raise two different exceptions, which represent the
158
+ # same thing. One is a Java exception, the other is a Ruby
159
+ # exception. It's probably a JRuby bug:
160
+ # https://github.com/jruby/jruby/issues/1903
161
+ begin
162
+ expect do
163
+ subject.call(notice)
164
+ end.to raise_error(SystemStackError)
165
+ rescue RSpec::Expectations::ExpectationNotMetError
166
+ expect do
167
+ subject.call(notice)
168
+ end.to raise_error(java.lang.StackOverflowError)
169
+ end
170
+ else
171
+ expect do
172
+ subject.call(notice)
173
+ end.to raise_error(SystemStackError)
174
+ end
175
+ end
176
+ end
177
+ end
178
+
179
+ describe "context payload" do
180
+ describe "URL" do
181
+ let(:patterns) { ['bish'] }
182
+
183
+ context "when it contains query params" do
184
+ it "filters the params" do
185
+ notice[:context][:url] = 'http://localhost:3000/crash?foo=bar&baz=bongo&bish=bash'
186
+ subject.call(notice)
187
+ expect(notice[:context][:url]).to(
188
+ eq('http://localhost:3000/crash?foo=[Filtered]&baz=[Filtered]&bish=bash')
189
+ )
190
+ end
191
+ end
192
+
193
+ context "when it is invalid" do
194
+ it "leaves the URL unfiltered" do
195
+ notice[:context][:url] =
196
+ 'http://localhost:3000/cra]]]sh?foo=bar&baz=bongo&bish=bash'
197
+ subject.call(notice)
198
+ expect(notice[:context][:url]).to(
199
+ eq('http://localhost:3000/cra]]]sh?foo=bar&baz=bongo&bish=bash')
200
+ )
201
+ end
202
+ end
203
+
204
+ context "when it is without a query" do
205
+ it "leaves the URL untouched" do
206
+ notice[:context][:url] = 'http://localhost:3000/crash'
207
+ subject.call(notice)
208
+ expect(notice[:context][:url]).to(eq('http://localhost:3000/crash'))
209
+ end
210
+ end
211
+ end
212
+ end
213
+ end