blinkist-airbrake-scrubber 2.1.2 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +6 -12
- data/blinkist-airbrake-scrubber.gemspec +4 -4
- data/lib/blinkist-airbrake-scrubber.rb +15 -17
- data/lib/blinkist-airbrake-scrubber/deep_traversal.rb +26 -28
- data/lib/blinkist-airbrake-scrubber/scrubbers/message_email.rb +11 -13
- data/lib/blinkist-airbrake-scrubber/scrubbers/params_email.rb +10 -12
- data/lib/blinkist-airbrake-scrubber/scrubbers/params_password.rb +10 -12
- data/lib/blinkist-airbrake-scrubber/version.rb +4 -4
- data/spec/specs/lib/blinkist-airbrake-scrubber/airbrake_spec.rb +4 -4
- data/spec/specs/lib/blinkist-airbrake-scrubber/deep_traversal_spec.rb +1 -1
- data/spec/specs/lib/blinkist-airbrake-scrubber/scrubbers/message_email_spec.rb +2 -2
- data/spec/specs/lib/blinkist-airbrake-scrubber/scrubbers/params_email_spec.rb +4 -4
- data/spec/specs/lib/blinkist-airbrake-scrubber/scrubbers/params_password_spec.rb +4 -4
- data/spec/specs/lib/blinkist_airbrake_scrubber_spec.rb +8 -8
- data/spec/specs/version_spec.rb +5 -5
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 6c7292d499d48e12d164b7099303bc900cb2386c
|
4
|
+
data.tar.gz: e5c1c74178c864d515b79e91c3cf2815da08584e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8a6c5b60ac452b1c6137dd00138db21b2bdfd403fb2ef62c5514b23dc6aefca20946ce0076581077fd6198b6e62f4626ce06385850f47cbce3bf62f69f83949c
|
7
|
+
data.tar.gz: ce775a0990fe520c9d7396df3da85c48e92b279b7faae5963e6d8da16db69c3c0b87230247d2e49c530190d8cac06a0b02f6bff7a677b0999bb42dc8c0f117bd
|
data/README.md
CHANGED
@@ -1,12 +1,12 @@
|
|
1
|
-
# Blinkist::
|
1
|
+
# Blinkist::AirbrakeScrubber
|
2
2
|
|
3
|
-
Blinkist::
|
3
|
+
Blinkist::AirbrakeScrubber provides an Airbrake scrubbing service to remove various sensitive informations from the notifications, e.g. emails. It does *not* replace Airbrake configuration, but provides some seamless functionality.
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
7
7
|
Add this line to your application's Gemfile:
|
8
8
|
|
9
|
-
gem "blinkist-airbrake-scrubber"
|
9
|
+
gem "blinkist-airbrake-scrubber"
|
10
10
|
|
11
11
|
And then execute:
|
12
12
|
|
@@ -19,7 +19,7 @@ And then execute:
|
|
19
19
|
To extend the functionality, create a new scrubber file in `lib/blinkist-airbrake-scrubber/scrubbers` folder, like the template:
|
20
20
|
|
21
21
|
```ruby
|
22
|
-
module Blinkist::
|
22
|
+
module Blinkist::AirbrakeScrubber
|
23
23
|
class WubbaLubba
|
24
24
|
REGEXP = /[\S]+@[\S]+/i
|
25
25
|
|
@@ -34,17 +34,11 @@ module Blinkist::Airbrake::Scrubber
|
|
34
34
|
end
|
35
35
|
```
|
36
36
|
|
37
|
-
Then, add the class to Blinkist::
|
38
|
-
|
39
|
-
### Applications using this gem
|
40
|
-
|
41
|
-
There are a few applications available on GitHub that use this gem. For usage see:
|
42
|
-
|
43
|
-
* blinkist-messenger
|
37
|
+
Then, add the class to Blinkist::AirbrakeScrubber's SCRUBBERS list to have it ran after Airbrake.configure
|
44
38
|
|
45
39
|
### Dependencies
|
46
40
|
|
47
|
-
This gem has dependency on airbrake (~>
|
41
|
+
This gem has dependency on airbrake (~> 7), it will automatically add it unless already bundled.
|
48
42
|
|
49
43
|
## Maintainers
|
50
44
|
|
@@ -5,12 +5,12 @@ require_relative "lib/blinkist-airbrake-scrubber/version"
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |gem|
|
7
7
|
gem.name = "blinkist-airbrake-scrubber"
|
8
|
-
gem.version = Blinkist::
|
9
|
-
gem.authors = ["Paweł Komarnicki", "Dinesh Vasudevan"]
|
10
|
-
gem.email = ["pawel@blinkist.com", "dinesh@blinkist.com"]
|
8
|
+
gem.version = Blinkist::AirbrakeScrubber::VERSION
|
9
|
+
gem.authors = ["Paweł Komarnicki", "Dinesh Vasudevan", 'Tomek Przedmojski']
|
10
|
+
gem.email = ["pawel@blinkist.com", "dinesh@blinkist.com", "tomek@blinkist.com"]
|
11
11
|
gem.description = %q{Email scrubbing configuration for Airbrake at Blinkist}
|
12
12
|
gem.summary = %q{With this, Airbrake will not leak emails via exception notifications}
|
13
|
-
gem.homepage = "https://github.com/blinkist/
|
13
|
+
gem.homepage = "https://github.com/blinkist/airbrake-scrubber"
|
14
14
|
gem.license = "MIT"
|
15
15
|
|
16
16
|
# Airbrake
|
@@ -4,32 +4,30 @@ Dir[File.expand_path('../../lib/**/*.rb', __FILE__)].each do |f|
|
|
4
4
|
end
|
5
5
|
|
6
6
|
# Prepend the original Airbrake module
|
7
|
-
# Note: Do not remove from this file, it needs to be before Blinkist::
|
7
|
+
# Note: Do not remove from this file, it needs to be before Blinkist::AirbrakeScrubber
|
8
8
|
module Airbrake
|
9
9
|
class << self
|
10
|
-
prepend Blinkist::
|
10
|
+
prepend Blinkist::AirbrakeScrubber
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
14
|
# Set up the namespace and run every scrubber listed in SCRUBBERS
|
15
15
|
module Blinkist
|
16
|
-
module
|
17
|
-
|
18
|
-
|
19
|
-
SCRUBBERS = [ MessageEmail, ParamsEmail, ParamsPassword ]
|
16
|
+
module AirbrakeScrubber
|
17
|
+
FILTERED = '[Filtered]'
|
18
|
+
SCRUBBERS = [ MessageEmail, ParamsEmail, ParamsPassword ]
|
20
19
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
# Run scrubbers
|
29
|
-
def self.run!
|
30
|
-
SCRUBBERS.each { |scrubber| scrubber::scrub! }
|
31
|
-
end
|
20
|
+
# Override original Airbrake.configure
|
21
|
+
def configure(*args, &block)
|
22
|
+
super
|
23
|
+
ensure
|
24
|
+
Blinkist::AirbrakeScrubber.run!
|
25
|
+
end
|
32
26
|
|
27
|
+
# Run scrubbers
|
28
|
+
def self.run!
|
29
|
+
SCRUBBERS.each { |scrubber| scrubber::scrub! }
|
33
30
|
end
|
31
|
+
|
34
32
|
end
|
35
33
|
end
|
@@ -1,43 +1,41 @@
|
|
1
1
|
# DeepTraversal provides traverse possibility of Hashes
|
2
2
|
# Can handle Hash objects with deep nesting, or other nested objects that can be dug deeper (e.g. Array)
|
3
3
|
module Blinkist
|
4
|
-
module
|
5
|
-
module Scrubber
|
4
|
+
module AirbrakeScrubber
|
6
5
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
6
|
+
class DeepTraversal
|
7
|
+
def initialize(source)
|
8
|
+
@source = source
|
9
|
+
end
|
11
10
|
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
def traverse(&block)
|
12
|
+
recursive_traverse(@source, &block)
|
13
|
+
end
|
15
14
|
|
16
|
-
|
15
|
+
private
|
17
16
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
17
|
+
def recursive_traverse(input, &block)
|
18
|
+
case input
|
19
|
+
when Array
|
20
|
+
input.map { |i| recursive_traverse(i, &block) }
|
22
21
|
|
23
|
-
|
24
|
-
|
22
|
+
when Hash
|
23
|
+
Hash[input.map { |key, value|
|
25
24
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
25
|
+
# Go deeper for things that are not simple objects
|
26
|
+
case value
|
27
|
+
when Array, Hash
|
28
|
+
[ key, recursive_traverse(value, &block) ]
|
29
|
+
else
|
30
|
+
[ key, block.call(key, value) ]
|
31
|
+
end
|
33
32
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
end
|
33
|
+
}]
|
34
|
+
else
|
35
|
+
input
|
38
36
|
end
|
39
37
|
end
|
40
|
-
|
41
38
|
end
|
39
|
+
|
42
40
|
end
|
43
41
|
end
|
@@ -1,21 +1,19 @@
|
|
1
1
|
module Blinkist
|
2
|
-
module
|
3
|
-
|
4
|
-
|
5
|
-
REGEXP = /[\S]+@[\S]+/i
|
2
|
+
module AirbrakeScrubber
|
3
|
+
class MessageEmail
|
4
|
+
REGEXP = /[\S]+@[\S]+/i
|
6
5
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
end
|
12
|
-
end # def self.scrub!
|
13
|
-
|
14
|
-
def self.scrub(message)
|
15
|
-
message.gsub(REGEXP, FILTERED)
|
6
|
+
def self.scrub!
|
7
|
+
::Airbrake.add_filter do |notice|
|
8
|
+
# Cannot do gsub! coz of frozen literals
|
9
|
+
notice[:errors].each { |error| error[:message] = scrub(error[:message]) }
|
16
10
|
end
|
11
|
+
end # def self.scrub!
|
17
12
|
|
13
|
+
def self.scrub(message)
|
14
|
+
message.gsub(REGEXP, FILTERED)
|
18
15
|
end
|
16
|
+
|
19
17
|
end
|
20
18
|
end
|
21
19
|
end
|
@@ -1,19 +1,17 @@
|
|
1
1
|
module Blinkist
|
2
|
-
module
|
3
|
-
|
4
|
-
class ParamsEmail
|
2
|
+
module AirbrakeScrubber
|
3
|
+
class ParamsEmail
|
5
4
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
end
|
12
|
-
notice
|
5
|
+
def self.scrub!
|
6
|
+
::Airbrake.add_filter do |notice|
|
7
|
+
notice[:params] = DeepTraversal.new(notice[:params]).traverse do |key, value|
|
8
|
+
value = FILTERED if key.to_s == 'email'
|
9
|
+
value
|
13
10
|
end
|
14
|
-
|
11
|
+
notice
|
12
|
+
end
|
13
|
+
end # def self.scrub!
|
15
14
|
|
16
|
-
end
|
17
15
|
end
|
18
16
|
end
|
19
17
|
end
|
@@ -1,19 +1,17 @@
|
|
1
1
|
module Blinkist
|
2
|
-
module
|
3
|
-
|
4
|
-
class ParamsPassword
|
2
|
+
module AirbrakeScrubber
|
3
|
+
class ParamsPassword
|
5
4
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
end
|
12
|
-
notice
|
5
|
+
def self.scrub!
|
6
|
+
::Airbrake.add_filter do |notice|
|
7
|
+
notice[:params] = DeepTraversal.new(notice[:params]).traverse do |key, value|
|
8
|
+
value = FILTERED if key.to_s == 'password'
|
9
|
+
value
|
13
10
|
end
|
14
|
-
|
11
|
+
notice
|
12
|
+
end
|
13
|
+
end # def self.scrub!
|
15
14
|
|
16
|
-
end
|
17
15
|
end
|
18
16
|
end
|
19
17
|
end
|
@@ -10,14 +10,14 @@ describe Airbrake do
|
|
10
10
|
}
|
11
11
|
|
12
12
|
describe "Module#prepend" do
|
13
|
-
it "does have Blinkist::
|
14
|
-
expect(described_class.ancestors).to include(Blinkist::
|
13
|
+
it "does have Blinkist::AirbrakeScrubber as one of the ancestors" do
|
14
|
+
expect(described_class.ancestors).to include(Blinkist::AirbrakeScrubber)
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
18
|
describe '.configure' do
|
19
|
-
it "does call Blinkist::
|
20
|
-
expect_any_instance_of(Blinkist::
|
19
|
+
it "does call Blinkist::AirbrakeScrubber.configure" do
|
20
|
+
expect_any_instance_of(Blinkist::AirbrakeScrubber).to receive(:configure)
|
21
21
|
instantiate_airbrake
|
22
22
|
end
|
23
23
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe Blinkist::
|
3
|
+
describe Blinkist::AirbrakeScrubber::MessageEmail do
|
4
4
|
|
5
5
|
describe "Structure" do
|
6
6
|
it "has REGEXP constant" do
|
@@ -24,7 +24,7 @@ describe Blinkist::Airbrake::Scrubber::MessageEmail do
|
|
24
24
|
# It's ridiculously hard to peek into Airbrake::Notice
|
25
25
|
# Instead verify the functionality here
|
26
26
|
describe ".scrub" do
|
27
|
-
let(:filtered) { Blinkist::
|
27
|
+
let(:filtered) { Blinkist::AirbrakeScrubber::FILTERED }
|
28
28
|
let(:regexp) { described_class::REGEXP }
|
29
29
|
|
30
30
|
let(:valid_domains) { %w{ example.org exam-ple.org exam.ple.org e-xam.ple.org e-xam.p-le.org e.x.a.m.p.l.e.co.uk } }
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe Blinkist::
|
3
|
+
describe Blinkist::AirbrakeScrubber::ParamsEmail do
|
4
4
|
let(:notifier) { Airbrake[:default] }
|
5
5
|
let(:notice) {
|
6
6
|
Airbrake[:default].build_notice(
|
@@ -23,7 +23,7 @@ describe Blinkist::Airbrake::Scrubber::ParamsEmail do
|
|
23
23
|
|
24
24
|
it "scrubs the email from the params hash" do
|
25
25
|
notifier.instance_variable_get(:@filter_chain).refine(notice)
|
26
|
-
expect(notice[:params][:email]).to eq(Blinkist::
|
26
|
+
expect(notice[:params][:email]).to eq(Blinkist::AirbrakeScrubber::FILTERED)
|
27
27
|
end
|
28
28
|
|
29
29
|
it "scrubs the deep-nested email from the params hash" do
|
@@ -33,8 +33,8 @@ describe Blinkist::Airbrake::Scrubber::ParamsEmail do
|
|
33
33
|
)
|
34
34
|
|
35
35
|
notifier.instance_variable_get(:@filter_chain).refine(notice)
|
36
|
-
expect(notice[:params][:email]).to eq(Blinkist::
|
37
|
-
expect(notice[:params][:deeply][:nested][:email]).to eq(Blinkist::
|
36
|
+
expect(notice[:params][:email]).to eq(Blinkist::AirbrakeScrubber::FILTERED)
|
37
|
+
expect(notice[:params][:deeply][:nested][:email]).to eq(Blinkist::AirbrakeScrubber::FILTERED)
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe Blinkist::
|
3
|
+
describe Blinkist::AirbrakeScrubber::ParamsPassword do
|
4
4
|
let(:notifier) { Airbrake[:default] }
|
5
5
|
let(:notice) {
|
6
6
|
Airbrake[:default].build_notice(
|
@@ -23,7 +23,7 @@ describe Blinkist::Airbrake::Scrubber::ParamsPassword do
|
|
23
23
|
|
24
24
|
it "scrubs the password from the params hash" do
|
25
25
|
notifier.instance_variable_get(:@filter_chain).refine(notice)
|
26
|
-
expect(notice[:params][:password]).to eq(Blinkist::
|
26
|
+
expect(notice[:params][:password]).to eq(Blinkist::AirbrakeScrubber::FILTERED)
|
27
27
|
end
|
28
28
|
|
29
29
|
it "scrubs the deep-nested password from the params hash" do
|
@@ -33,8 +33,8 @@ describe Blinkist::Airbrake::Scrubber::ParamsPassword do
|
|
33
33
|
)
|
34
34
|
|
35
35
|
notifier.instance_variable_get(:@filter_chain).refine(notice)
|
36
|
-
expect(notice[:params][:password]).to eq(Blinkist::
|
37
|
-
expect(notice[:params][:deeply][:nested][:password]).to eq(Blinkist::
|
36
|
+
expect(notice[:params][:password]).to eq(Blinkist::AirbrakeScrubber::FILTERED)
|
37
|
+
expect(notice[:params][:deeply][:nested][:password]).to eq(Blinkist::AirbrakeScrubber::FILTERED)
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
@@ -2,7 +2,7 @@ require 'spec_helper'
|
|
2
2
|
require 'securerandom'
|
3
3
|
require 'blinkist-airbrake-scrubber'
|
4
4
|
|
5
|
-
describe Blinkist::
|
5
|
+
describe Blinkist::AirbrakeScrubber do
|
6
6
|
let(:instantiate_airbrake) {
|
7
7
|
Airbrake.configure :"notifier_#{ SecureRandom.uuid }" do |c|
|
8
8
|
c.project_id = 1
|
@@ -12,19 +12,19 @@ describe Blinkist::Airbrake::Scrubber do
|
|
12
12
|
|
13
13
|
describe "Constants" do
|
14
14
|
it "has FILTERED constant" do
|
15
|
-
expect(Blinkist::
|
15
|
+
expect(Blinkist::AirbrakeScrubber.constants).to include(:FILTERED)
|
16
16
|
end
|
17
17
|
|
18
18
|
it "has explicit FILTERED constant content" do
|
19
|
-
expect(Blinkist::
|
19
|
+
expect(Blinkist::AirbrakeScrubber::FILTERED).to eq('[Filtered]')
|
20
20
|
end
|
21
21
|
|
22
22
|
it "has SCRUBBERS constant" do
|
23
|
-
expect(Blinkist::
|
23
|
+
expect(Blinkist::AirbrakeScrubber.constants).to include(:SCRUBBERS)
|
24
24
|
end
|
25
25
|
|
26
26
|
it "has list of scrubbers in SCRUBBERS" do
|
27
|
-
expect(Blinkist::
|
27
|
+
expect(Blinkist::AirbrakeScrubber::SCRUBBERS.is_a?(Array)).to be true
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
@@ -34,15 +34,15 @@ describe Blinkist::Airbrake::Scrubber do
|
|
34
34
|
instantiate_airbrake
|
35
35
|
end
|
36
36
|
|
37
|
-
it "calls Blinkist::
|
38
|
-
expect(Blinkist::
|
37
|
+
it "calls Blinkist::AirbrakeScrubber.run!" do
|
38
|
+
expect(Blinkist::AirbrakeScrubber).to receive(:run!)
|
39
39
|
instantiate_airbrake
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
43
|
describe "self.run!" do
|
44
44
|
it "runs ::scrub! for every scrubber declared in SCRUBBERS" do
|
45
|
-
Blinkist::
|
45
|
+
Blinkist::AirbrakeScrubber::SCRUBBERS.each do |scrubber|
|
46
46
|
expect(scrubber).to receive(:scrub!)
|
47
47
|
end
|
48
48
|
instantiate_airbrake
|
data/spec/specs/version_spec.rb
CHANGED
@@ -2,17 +2,17 @@
|
|
2
2
|
require 'spec_helper'
|
3
3
|
require 'blinkist-airbrake-scrubber/version'
|
4
4
|
|
5
|
-
describe Blinkist::
|
5
|
+
describe Blinkist::AirbrakeScrubber::VERSION do
|
6
6
|
|
7
7
|
it 'provides the current version' do
|
8
|
-
version = Blinkist::
|
8
|
+
version = Blinkist::AirbrakeScrubber::VERSION
|
9
9
|
expect(version).to_not be nil
|
10
10
|
expect(version.instance_of?(String)).to be true
|
11
11
|
end
|
12
12
|
|
13
|
-
it 'equals
|
14
|
-
version = Blinkist::
|
15
|
-
expect(version).to eq '
|
13
|
+
it 'equals 3.0.0 for auto-check purposes' do
|
14
|
+
version = Blinkist::AirbrakeScrubber::VERSION
|
15
|
+
expect(version).to eq '3.0.0'
|
16
16
|
end
|
17
17
|
|
18
18
|
end
|
metadata
CHANGED
@@ -1,15 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: blinkist-airbrake-scrubber
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paweł Komarnicki
|
8
8
|
- Dinesh Vasudevan
|
9
|
+
- Tomek Przedmojski
|
9
10
|
autorequire:
|
10
11
|
bindir: bin
|
11
12
|
cert_chain: []
|
12
|
-
date: 2018-05-
|
13
|
+
date: 2018-05-28 00:00:00.000000000 Z
|
13
14
|
dependencies:
|
14
15
|
- !ruby/object:Gem::Dependency
|
15
16
|
name: airbrake
|
@@ -29,6 +30,7 @@ description: Email scrubbing configuration for Airbrake at Blinkist
|
|
29
30
|
email:
|
30
31
|
- pawel@blinkist.com
|
31
32
|
- dinesh@blinkist.com
|
33
|
+
- tomek@blinkist.com
|
32
34
|
executables: []
|
33
35
|
extensions: []
|
34
36
|
extra_rdoc_files: []
|
@@ -51,7 +53,7 @@ files:
|
|
51
53
|
- spec/specs/lib/blinkist-airbrake-scrubber/scrubbers/params_password_spec.rb
|
52
54
|
- spec/specs/lib/blinkist_airbrake_scrubber_spec.rb
|
53
55
|
- spec/specs/version_spec.rb
|
54
|
-
homepage: https://github.com/blinkist/
|
56
|
+
homepage: https://github.com/blinkist/airbrake-scrubber
|
55
57
|
licenses:
|
56
58
|
- MIT
|
57
59
|
metadata: {}
|
@@ -71,7 +73,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
71
73
|
version: '0'
|
72
74
|
requirements: []
|
73
75
|
rubyforge_project:
|
74
|
-
rubygems_version: 2.
|
76
|
+
rubygems_version: 2.5.2.2
|
75
77
|
signing_key:
|
76
78
|
specification_version: 4
|
77
79
|
summary: With this, Airbrake will not leak emails via exception notifications
|