sentry-raven 2.9.0 → 3.0.1

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 (43) hide show
  1. checksums.yaml +5 -5
  2. data/.craft.yml +14 -0
  3. data/.github/workflows/test.yml +77 -0
  4. data/.gitignore +2 -0
  5. data/.rubocop.yml +44 -9
  6. data/.scripts/bump-version.sh +9 -0
  7. data/Gemfile +17 -25
  8. data/README.md +5 -4
  9. data/changelog.md +77 -1
  10. data/lib/raven/backtrace.rb +7 -5
  11. data/lib/raven/base.rb +5 -3
  12. data/lib/raven/breadcrumbs/activesupport.rb +10 -10
  13. data/lib/raven/breadcrumbs/logger.rb +4 -4
  14. data/lib/raven/breadcrumbs.rb +1 -1
  15. data/lib/raven/cli.rb +2 -2
  16. data/lib/raven/client.rb +28 -10
  17. data/lib/raven/configuration.rb +23 -8
  18. data/lib/raven/event.rb +5 -9
  19. data/lib/raven/instance.rb +12 -3
  20. data/lib/raven/integrations/delayed_job.rb +14 -15
  21. data/lib/raven/integrations/rack-timeout.rb +2 -3
  22. data/lib/raven/integrations/rack.rb +4 -3
  23. data/lib/raven/integrations/rails/active_job.rb +10 -7
  24. data/lib/raven/integrations/rails/controller_transaction.rb +1 -1
  25. data/lib/raven/integrations/rails/overrides/debug_exceptions_catcher.rb +2 -2
  26. data/lib/raven/integrations/rails.rb +1 -0
  27. data/lib/raven/interface.rb +2 -2
  28. data/lib/raven/interfaces/stack_trace.rb +1 -1
  29. data/lib/raven/linecache.rb +5 -2
  30. data/lib/raven/logger.rb +3 -2
  31. data/lib/raven/processor/cookies.rb +16 -6
  32. data/lib/raven/processor/post_data.rb +2 -0
  33. data/lib/raven/processor/removecircularreferences.rb +1 -0
  34. data/lib/raven/processor/sanitizedata.rb +65 -17
  35. data/lib/raven/processor/utf8conversion.rb +3 -1
  36. data/lib/raven/transports/http.rb +5 -5
  37. data/lib/raven/transports.rb +4 -0
  38. data/lib/raven/utils/exception_cause_chain.rb +1 -0
  39. data/lib/raven/utils/real_ip.rb +1 -1
  40. data/lib/raven/version.rb +2 -2
  41. data/sentry-raven.gemspec +3 -3
  42. metadata +8 -13
  43. data/.travis.yml +0 -47
@@ -9,19 +9,22 @@ module Raven
9
9
  def self.included(base)
10
10
  base.class_eval do
11
11
  around_perform do |job, block|
12
- capture_and_reraise_with_sentry(job, block)
12
+ if already_supported_by_specific_integration?(job)
13
+ block.call
14
+ else
15
+ capture_and_reraise_with_sentry(job, block)
16
+ end
13
17
  end
14
18
  end
15
19
  end
16
20
 
17
21
  def capture_and_reraise_with_sentry(job, block)
18
22
  block.call
19
- rescue Exception => exception # rubocop:disable Lint/RescueException
20
- return if rescue_with_handler(exception)
21
- unless already_supported_by_specific_integration?(job)
22
- Raven.capture_exception(exception, :extra => raven_context(job))
23
- end
24
- raise exception
23
+ rescue Exception => e # rubocop:disable Lint/RescueException
24
+ return if rescue_with_handler(e)
25
+
26
+ Raven.capture_exception(e, :extra => raven_context(job))
27
+ raise e
25
28
  ensure
26
29
  Context.clear!
27
30
  BreadcrumbBuffer.clear!
@@ -2,7 +2,7 @@ module Raven
2
2
  class Rails
3
3
  module ControllerTransaction
4
4
  def self.included(base)
5
- base.around_action do |controller, block|
5
+ base.prepend_around_action do |controller, block|
6
6
  Raven.context.transaction.push "#{controller.class}##{controller.action_name}"
7
7
  block.call
8
8
  Raven.context.transaction.pop
@@ -6,7 +6,7 @@ module Raven
6
6
  begin
7
7
  env = env_or_request.respond_to?(:env) ? env_or_request.env : env_or_request
8
8
  Raven::Rack.capture_exception(exception, env)
9
- rescue # rubocop:disable Lint/HandleExceptions
9
+ rescue
10
10
  end
11
11
  super
12
12
  end
@@ -21,7 +21,7 @@ module Raven
21
21
  begin
22
22
  env = env_or_request.respond_to?(:env) ? env_or_request.env : env_or_request
23
23
  Raven::Rack.capture_exception(exception, env)
24
- rescue # rubocop:disable Lint/HandleExceptions
24
+ rescue
25
25
  end
26
26
  render_exception_without_raven(env_or_request, exception)
27
27
  end
@@ -5,6 +5,7 @@ module Raven
5
5
  require 'raven/integrations/rails/overrides/streaming_reporter'
6
6
  require 'raven/integrations/rails/controller_methods'
7
7
  require 'raven/integrations/rails/controller_transaction'
8
+ require 'raven/integrations/rack'
8
9
 
9
10
  initializer "raven.use_rack_middleware" do |app|
10
11
  app.config.middleware.insert 0, Raven::Rack
@@ -1,9 +1,9 @@
1
1
  module Raven
2
2
  class Interface
3
3
  def initialize(attributes = nil)
4
- attributes.each do |attr, value|
4
+ attributes&.each do |attr, value|
5
5
  public_send "#{attr}=", value
6
- end if attributes
6
+ end
7
7
 
8
8
  yield self if block_given?
9
9
  end
@@ -58,7 +58,7 @@ module Raven
58
58
  end
59
59
 
60
60
  def project_root
61
- @project_root ||= Raven.configuration.project_root && Raven.configuration.project_root.to_s
61
+ @project_root ||= Raven.configuration.project_root&.to_s
62
62
  end
63
63
 
64
64
  def longest_load_path
@@ -10,6 +10,7 @@ module Raven
10
10
  # line should be the line requested by lineno. See specs for more information.
11
11
  def get_file_context(filename, lineno, context)
12
12
  return nil, nil, nil unless valid_path?(filename)
13
+
13
14
  lines = Array.new(2 * context + 1) do |i|
14
15
  getline(filename, lineno - context + i)
15
16
  end
@@ -26,15 +27,17 @@ module Raven
26
27
  def getlines(path)
27
28
  @cache[path] ||= begin
28
29
  IO.readlines(path)
29
- rescue
30
- nil
30
+ rescue
31
+ nil
31
32
  end
32
33
  end
33
34
 
34
35
  def getline(path, n)
35
36
  return nil if n < 1
37
+
36
38
  lines = getlines(path)
37
39
  return nil if lines.nil?
40
+
38
41
  lines[n - 1]
39
42
  end
40
43
  end
data/lib/raven/logger.rb CHANGED
@@ -1,10 +1,11 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'logger'
3
4
 
4
5
  module Raven
5
6
  class Logger < ::Logger
6
- LOG_PREFIX = "** [Raven] ".freeze
7
- PROGNAME = "sentry".freeze
7
+ LOG_PREFIX = "** [Raven] "
8
+ PROGNAME = "sentry"
8
9
 
9
10
  def initialize(*)
10
11
  super
@@ -10,17 +10,27 @@ module Raven
10
10
  private
11
11
 
12
12
  def process_if_symbol_keys(data)
13
- data[:request][:cookies] = STRING_MASK if data[:request][:cookies]
13
+ if cookies = data.dig(:request, :cookies)
14
+ data[:request][:cookies] = generate_masked_cookies(cookies)
15
+ end
14
16
 
15
- return unless data[:request][:headers] && data[:request][:headers]["Cookie"]
16
- data[:request][:headers]["Cookie"] = STRING_MASK
17
+ if cookies_header = data[:request][:headers]["Cookie"]
18
+ data[:request][:headers]["Cookie"] = generate_masked_cookies(cookies_header)
19
+ end
17
20
  end
18
21
 
19
22
  def process_if_string_keys(data)
20
- data["request"]["cookies"] = STRING_MASK if data["request"]["cookies"]
23
+ if cookies = data.dig("request", "cookies")
24
+ data["request"]["cookies"] = generate_masked_cookies(cookies)
25
+ end
21
26
 
22
- return unless data["request"]["headers"] && data["request"]["headers"]["Cookie"]
23
- data["request"]["headers"]["Cookie"] = STRING_MASK
27
+ if cookies_header = data.dig("request", "headers", "Cookie")
28
+ data["request"]["headers"]["Cookie"] = generate_masked_cookies(cookies_header)
29
+ end
30
+ end
31
+
32
+ def generate_masked_cookies(cookies)
33
+ cookies.merge(cookies) { STRING_MASK }
24
34
  end
25
35
  end
26
36
  end
@@ -11,11 +11,13 @@ module Raven
11
11
 
12
12
  def process_if_symbol_keys(data)
13
13
  return unless data[:request][:method] == "POST"
14
+
14
15
  data[:request][:data] = STRING_MASK
15
16
  end
16
17
 
17
18
  def process_if_string_keys(data)
18
19
  return unless data["request"]["method"] == "POST"
20
+
19
21
  data["request"]["data"] = STRING_MASK
20
22
  end
21
23
  end
@@ -2,6 +2,7 @@ module Raven
2
2
  class Processor::RemoveCircularReferences < Processor
3
3
  def process(value, visited = [])
4
4
  return "(...)" if visited.include?(value.__id__)
5
+
5
6
  visited << value.__id__ if value.is_a?(Array) || value.is_a?(Hash)
6
7
 
7
8
  case value
@@ -1,10 +1,11 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'json'
3
4
 
4
5
  module Raven
5
6
  class Processor::SanitizeData < Processor
6
7
  DEFAULT_FIELDS = %w(authorization password passwd secret ssn social(.*)?sec).freeze
7
- CREDIT_CARD_RE = /\b(?:3[47]\d|(?:4\d|5[1-5]|65)\d{2}|6011)\d{12}\b/
8
+ CREDIT_CARD_RE = /\b(?:3[47]\d|(?:4\d|5[1-5]|65)\d{2}|6011)\d{12}\b/.freeze
8
9
  QUERY_STRING = ['query_string', :query_string].freeze
9
10
  JSON_STARTS_WITH = ["[", "{"].freeze
10
11
 
@@ -20,22 +21,13 @@ module Raven
20
21
  def process(value, key = nil)
21
22
  case value
22
23
  when Hash
23
- !value.frozen? ? value.merge!(value) { |k, v| process v, k } : value.merge(value) { |k, v| process v, k }
24
+ sanitize_hash_value(key, value)
24
25
  when Array
25
- !value.frozen? ? value.map! { |v| process v, key } : value.map { |v| process v, key }
26
+ sanitize_array_value(key, value)
26
27
  when Integer
27
28
  matches_regexes?(key, value.to_s) ? INT_MASK : value
28
29
  when String
29
- if value =~ fields_re && (json = parse_json_or_nil(value))
30
- # if this string is actually a json obj, convert and sanitize
31
- process(json).to_json
32
- elsif matches_regexes?(key, value)
33
- STRING_MASK
34
- elsif QUERY_STRING.include?(key)
35
- sanitize_query_string(value)
36
- else
37
- value
38
- end
30
+ sanitize_string_value(key, value)
39
31
  else
40
32
  value
41
33
  end
@@ -49,6 +41,39 @@ module Raven
49
41
  @utf8_processor ||= Processor::UTF8Conversion.new
50
42
  end
51
43
 
44
+ def sanitize_hash_value(key, value)
45
+ if key =~ sensitive_fields
46
+ STRING_MASK
47
+ elsif value.frozen?
48
+ value.merge(value) { |k, v| process v, k }
49
+ else
50
+ value.merge!(value) { |k, v| process v, k }
51
+ end
52
+ end
53
+
54
+ def sanitize_array_value(key, value)
55
+ if value.frozen?
56
+ value.map { |v| process v, key }
57
+ else
58
+ value.map! { |v| process v, key }
59
+ end
60
+ end
61
+
62
+ def sanitize_string_value(key, value)
63
+ if value =~ sensitive_fields && (json = parse_json_or_nil(value))
64
+ # if this string is actually a json obj, convert and sanitize
65
+ process(json).to_json
66
+ elsif matches_regexes?(key, value)
67
+ STRING_MASK
68
+ elsif QUERY_STRING.include?(key)
69
+ sanitize_query_string(value)
70
+ elsif value =~ sensitive_fields
71
+ sanitize_sensitive_string_content(value)
72
+ else
73
+ value
74
+ end
75
+ end
76
+
52
77
  def sanitize_query_string(query_string)
53
78
  query_hash = CGI.parse(query_string)
54
79
  sanitized = utf8_processor.process(query_hash)
@@ -56,16 +81,38 @@ module Raven
56
81
  URI.encode_www_form(processed_query_hash)
57
82
  end
58
83
 
84
+ # this scrubs some sensitive info from the string content. for example:
85
+ #
86
+ # ```
87
+ # unexpected token at '{
88
+ # "role": "admin","password": "Abc@123","foo": "bar"
89
+ # }'
90
+ # ```
91
+ #
92
+ # will become
93
+ #
94
+ # ```
95
+ # unexpected token at '{
96
+ # "role": "admin","password": *******,"foo": "bar"
97
+ # }'
98
+ # ```
99
+ #
100
+ # it's particularly useful in hash or param-parsing related errors
101
+ def sanitize_sensitive_string_content(value)
102
+ value.gsub(/(#{sensitive_fields}['":]\s?(:|=>)?\s?)(".*?"|'.*?')/, '\1' + STRING_MASK)
103
+ end
104
+
59
105
  def matches_regexes?(k, v)
60
106
  (sanitize_credit_cards && v =~ CREDIT_CARD_RE) ||
61
- k =~ fields_re
107
+ k =~ sensitive_fields
62
108
  end
63
109
 
64
- def fields_re
65
- return @fields_re if instance_variable_defined?(:@fields_re)
110
+ def sensitive_fields
111
+ return @sensitive_fields if instance_variable_defined?(:@sensitive_fields)
112
+
66
113
  fields = DEFAULT_FIELDS | sanitize_fields
67
114
  fields -= sanitize_fields_excluded
68
- @fields_re = /#{fields.map do |f|
115
+ @sensitive_fields = /#{fields.map do |f|
69
116
  use_boundary?(f) ? "\\b#{f}\\b" : f
70
117
  end.join("|")}/i
71
118
  end
@@ -80,6 +127,7 @@ module Raven
80
127
 
81
128
  def parse_json_or_nil(string)
82
129
  return unless string.start_with?(*JSON_STARTS_WITH)
130
+
83
131
  JSON.parse(string)
84
132
  rescue JSON::ParserError, NoMethodError
85
133
  nil
@@ -14,6 +14,7 @@ module Raven
14
14
  !value.frozen? ? value.map! { |v| process v } : value.map { |v| process v }
15
15
  when Exception
16
16
  return value if value.message.valid_encoding?
17
+
17
18
  clean_exc = value.class.new(remove_invalid_bytes(value.message))
18
19
  clean_exc.set_backtrace(value.backtrace)
19
20
  clean_exc
@@ -27,6 +28,7 @@ module Raven
27
28
  value.force_encoding(Encoding::UTF_8)
28
29
  end
29
30
  return value if value.valid_encoding?
31
+
30
32
  remove_invalid_bytes(value)
31
33
  else
32
34
  value
@@ -39,7 +41,7 @@ module Raven
39
41
  # https://github.com/rspec/rspec-support/blob/f0af3fd74a94ff7bb700f6ba06dbdc67bba17fbf/lib/rspec/support/encoded_string.rb#L120-L139
40
42
  if String.method_defined?(:scrub) # 2.1+
41
43
  def remove_invalid_bytes(string)
42
- string.scrub!(REPLACE)
44
+ string.scrub(REPLACE)
43
45
  end
44
46
  else
45
47
  def remove_invalid_bytes(string)
@@ -26,10 +26,10 @@ module Raven
26
26
  req.headers['X-Sentry-Auth'] = auth_header
27
27
  req.body = data
28
28
  end
29
- rescue Faraday::Error => ex
30
- error_info = ex.message
31
- if ex.response && ex.response[:headers]['x-sentry-error']
32
- error_info += " Error in headers is: #{ex.response[:headers]['x-sentry-error']}"
29
+ rescue Faraday::Error => e
30
+ error_info = e.message
31
+ if e.response && e.response[:headers]['x-sentry-error']
32
+ error_info += " Error in headers is: #{e.response[:headers]['x-sentry-error']}"
33
33
  end
34
34
  raise Raven::Error, error_info
35
35
  end
@@ -42,7 +42,7 @@ module Raven
42
42
  proxy = configuration.public_send(:proxy)
43
43
 
44
44
  Faraday.new(configuration.server, :ssl => ssl_configuration, :proxy => proxy) do |builder|
45
- configuration.faraday_builder.call(builder) if configuration.faraday_builder
45
+ configuration.faraday_builder&.call(builder)
46
46
  builder.response :raise_error
47
47
  builder.options.merge! faraday_opts
48
48
  builder.headers[:user_agent] = "sentry-ruby/#{Raven::VERSION}"
@@ -13,3 +13,7 @@ module Raven
13
13
  end
14
14
  end
15
15
  end
16
+
17
+ require "raven/transports/dummy"
18
+ require "raven/transports/http"
19
+ require "raven/transports/stdout"
@@ -7,6 +7,7 @@ module Raven
7
7
  while exception.cause
8
8
  exception = exception.cause
9
9
  break if exceptions.any? { |e| e.object_id == exception.object_id }
10
+
10
11
  exceptions << exception
11
12
  end
12
13
  exceptions
@@ -13,7 +13,7 @@ module Raven
13
13
  "fc00::/7", # private IPv6 range fc00::/7
14
14
  "10.0.0.0/8", # private IPv4 range 10.x.x.x
15
15
  "172.16.0.0/12", # private IPv4 range 172.16.0.0 .. 172.31.255.255
16
- "192.168.0.0/16", # private IPv4 range 192.168.x.x
16
+ "192.168.0.0/16" # private IPv4 range 192.168.x.x
17
17
  ].map { |proxy| IPAddr.new(proxy) }
18
18
 
19
19
  attr_accessor :ip, :ip_addresses
data/lib/raven/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Raven
3
- # Freezing this constant breaks in 1.9.x
4
- VERSION = "2.9.0" # rubocop:disable Style/MutableConstant
4
+ VERSION = "3.0.1"
5
5
  end
data/sentry-raven.gemspec CHANGED
@@ -5,17 +5,17 @@ Gem::Specification.new do |gem|
5
5
  gem.name = "sentry-raven"
6
6
  gem.authors = ["Sentry Team"]
7
7
  gem.description = gem.summary = "A gem that provides a client interface for the Sentry error logger"
8
- gem.email = "getsentry@googlegroups.com"
8
+ gem.email = "accounts@sentry.io"
9
9
  gem.license = 'Apache-2.0'
10
10
  gem.homepage = "https://github.com/getsentry/raven-ruby"
11
11
 
12
12
  gem.version = Raven::VERSION
13
13
  gem.platform = Gem::Platform::RUBY
14
- gem.required_ruby_version = '>= 1.9.0'
14
+ gem.required_ruby_version = '>= 2.3'
15
15
  gem.extra_rdoc_files = ["README.md", "LICENSE"]
16
16
  gem.files = `git ls-files | grep -Ev '^(spec|benchmarks|examples)'`.split("\n")
17
17
  gem.bindir = "exe"
18
18
  gem.executables = "raven"
19
19
 
20
- gem.add_dependency "faraday", ">= 0.7.6", "< 1.0"
20
+ gem.add_dependency "faraday", ">= 1.0"
21
21
  end
metadata CHANGED
@@ -1,23 +1,20 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sentry-raven
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.9.0
4
+ version: 3.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sentry Team
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-01-22 00:00:00.000000000 Z
11
+ date: 2020-08-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">="
18
- - !ruby/object:Gem::Version
19
- version: 0.7.6
20
- - - "<"
21
18
  - !ruby/object:Gem::Version
22
19
  version: '1.0'
23
20
  type: :runtime
@@ -25,13 +22,10 @@ dependencies:
25
22
  version_requirements: !ruby/object:Gem::Requirement
26
23
  requirements:
27
24
  - - ">="
28
- - !ruby/object:Gem::Version
29
- version: 0.7.6
30
- - - "<"
31
25
  - !ruby/object:Gem::Version
32
26
  version: '1.0'
33
27
  description: A gem that provides a client interface for the Sentry error logger
34
- email: getsentry@googlegroups.com
28
+ email: accounts@sentry.io
35
29
  executables:
36
30
  - raven
37
31
  extensions: []
@@ -39,11 +33,13 @@ extra_rdoc_files:
39
33
  - README.md
40
34
  - LICENSE
41
35
  files:
36
+ - ".craft.yml"
37
+ - ".github/workflows/test.yml"
42
38
  - ".gitignore"
43
39
  - ".gitmodules"
44
40
  - ".rspec"
45
41
  - ".rubocop.yml"
46
- - ".travis.yml"
42
+ - ".scripts/bump-version.sh"
47
43
  - Gemfile
48
44
  - LICENSE
49
45
  - README.md
@@ -114,15 +110,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
114
110
  requirements:
115
111
  - - ">="
116
112
  - !ruby/object:Gem::Version
117
- version: 1.9.0
113
+ version: '2.3'
118
114
  required_rubygems_version: !ruby/object:Gem::Requirement
119
115
  requirements:
120
116
  - - ">="
121
117
  - !ruby/object:Gem::Version
122
118
  version: '0'
123
119
  requirements: []
124
- rubyforge_project:
125
- rubygems_version: 2.5.2.3
120
+ rubygems_version: 3.0.3
126
121
  signing_key:
127
122
  specification_version: 4
128
123
  summary: A gem that provides a client interface for the Sentry error logger