airbrake-ruby 2.2.3 → 2.2.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -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