sentry-raven 1.1.0 → 3.1.2

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 (69) hide show
  1. checksums.yaml +5 -5
  2. data/.craft.yml +19 -0
  3. data/.scripts/bump-version.rb +5 -0
  4. data/CHANGELOG.md +703 -0
  5. data/Gemfile +37 -0
  6. data/Makefile +3 -0
  7. data/README.md +116 -18
  8. data/Rakefile +30 -0
  9. data/exe/raven +32 -0
  10. data/lib/raven/backtrace.rb +17 -8
  11. data/lib/raven/base.rb +45 -194
  12. data/lib/raven/breadcrumbs/active_support_logger.rb +25 -0
  13. data/lib/raven/breadcrumbs/logger.rb +3 -0
  14. data/lib/raven/breadcrumbs/sentry_logger.rb +73 -0
  15. data/lib/raven/breadcrumbs.rb +76 -0
  16. data/lib/raven/cli.rb +31 -39
  17. data/lib/raven/client.rb +45 -32
  18. data/lib/raven/configuration.rb +427 -130
  19. data/lib/raven/context.rb +33 -6
  20. data/lib/raven/core_ext/object/deep_dup.rb +57 -0
  21. data/lib/raven/core_ext/object/duplicable.rb +153 -0
  22. data/lib/raven/event.rb +194 -206
  23. data/lib/raven/helpers/deprecation_helper.rb +17 -0
  24. data/lib/raven/instance.rb +249 -0
  25. data/lib/raven/integrations/delayed_job.rb +25 -23
  26. data/lib/raven/integrations/rack-timeout.rb +22 -0
  27. data/lib/raven/integrations/rack.rb +40 -24
  28. data/lib/raven/integrations/rails/active_job.rb +52 -20
  29. data/lib/raven/integrations/rails/backtrace_cleaner.rb +29 -0
  30. data/lib/raven/integrations/rails/controller_transaction.rb +13 -0
  31. data/lib/raven/integrations/rails/overrides/debug_exceptions_catcher.rb +2 -2
  32. data/lib/raven/integrations/rails.rb +39 -7
  33. data/lib/raven/integrations/rake.rb +7 -2
  34. data/lib/raven/integrations/sidekiq/cleanup_middleware.rb +13 -0
  35. data/lib/raven/integrations/sidekiq/error_handler.rb +38 -0
  36. data/lib/raven/integrations/sidekiq.rb +6 -48
  37. data/lib/raven/integrations/tasks.rb +1 -1
  38. data/lib/raven/interface.rb +25 -0
  39. data/lib/raven/interfaces/exception.rb +5 -8
  40. data/lib/raven/interfaces/http.rb +5 -12
  41. data/lib/raven/interfaces/message.rb +10 -6
  42. data/lib/raven/interfaces/single_exception.rb +1 -5
  43. data/lib/raven/interfaces/stack_trace.rb +23 -30
  44. data/lib/raven/linecache.rb +35 -23
  45. data/lib/raven/logger.rb +13 -16
  46. data/lib/raven/processor/cookies.rb +27 -7
  47. data/lib/raven/processor/http_headers.rb +55 -0
  48. data/lib/raven/processor/post_data.rb +16 -3
  49. data/lib/raven/processor/removecircularreferences.rb +12 -8
  50. data/lib/raven/processor/removestacktrace.rb +17 -6
  51. data/lib/raven/processor/sanitizedata.rb +92 -37
  52. data/lib/raven/processor/utf8conversion.rb +39 -14
  53. data/lib/raven/processor.rb +5 -1
  54. data/lib/raven/transports/http.rb +31 -22
  55. data/lib/raven/transports/stdout.rb +20 -0
  56. data/lib/raven/transports.rb +6 -10
  57. data/lib/raven/utils/context_filter.rb +42 -0
  58. data/lib/raven/utils/deep_merge.rb +6 -12
  59. data/lib/raven/utils/exception_cause_chain.rb +20 -0
  60. data/lib/raven/utils/real_ip.rb +62 -0
  61. data/lib/raven/utils/request_id.rb +16 -0
  62. data/lib/raven/version.rb +2 -1
  63. data/lib/sentry-raven-without-integrations.rb +6 -1
  64. data/lib/sentry_raven_without_integrations.rb +1 -0
  65. data/sentry-raven.gemspec +28 -0
  66. metadata +44 -127
  67. data/lib/raven/error.rb +0 -4
  68. data/lib/raven/interfaces.rb +0 -34
  69. data/lib/raven/okjson.rb +0 -614
@@ -4,6 +4,9 @@ module Raven
4
4
  class Rails < ::Rails::Railtie
5
5
  require 'raven/integrations/rails/overrides/streaming_reporter'
6
6
  require 'raven/integrations/rails/controller_methods'
7
+ require 'raven/integrations/rails/controller_transaction'
8
+ require 'raven/integrations/rails/backtrace_cleaner'
9
+ require 'raven/integrations/rack'
7
10
 
8
11
  initializer "raven.use_rack_middleware" do |app|
9
12
  app.config.middleware.insert 0, Raven::Rack
@@ -12,27 +15,45 @@ module Raven
12
15
  initializer 'raven.action_controller' do
13
16
  ActiveSupport.on_load :action_controller do
14
17
  include Raven::Rails::ControllerMethods
18
+ include Raven::Rails::ControllerTransaction
15
19
  if ::Rails::VERSION::STRING >= "4.0.0"
16
- Raven.rails_safely_prepend("StreamingReporter", :to => ActionController::Live)
20
+ Raven.safely_prepend(
21
+ "StreamingReporter",
22
+ :from => Raven::Rails::Overrides,
23
+ :to => ActionController::Live
24
+ )
17
25
  end
18
26
  end
19
27
  end
20
28
 
21
29
  initializer 'raven.action_view' do
22
30
  ActiveSupport.on_load :action_view do
23
- Raven.rails_safely_prepend("StreamingReporter", :to => ActionView::StreamingTemplateRenderer::Body)
31
+ Raven.safely_prepend(
32
+ "StreamingReporter",
33
+ :from => Raven::Rails::Overrides,
34
+ :to => ActionView::StreamingTemplateRenderer::Body
35
+ )
24
36
  end
25
37
  end
26
38
 
27
39
  config.before_initialize do
28
- Raven.configure do |config|
29
- config.logger ||= ::Rails.logger
30
- config.project_root ||= ::Rails.root
31
- config.release = config.detect_release # if project_root has changed, need to re-check
40
+ Raven.configuration.logger = ::Rails.logger
41
+
42
+ backtrace_cleaner = Raven::Rails::BacktraceCleaner.new
43
+
44
+ Raven.configuration.backtrace_cleanup_callback = lambda do |backtrace|
45
+ backtrace_cleaner.clean(backtrace)
32
46
  end
33
47
  end
34
48
 
35
49
  config.after_initialize do
50
+ if Raven.configuration.breadcrumbs_logger.include?(:active_support_logger) ||
51
+ Raven.configuration.rails_activesupport_breadcrumbs
52
+
53
+ require 'raven/breadcrumbs/active_support_logger'
54
+ Raven::Breadcrumbs::ActiveSupportLogger.inject
55
+ end
56
+
36
57
  if Raven.configuration.rails_report_rescued_exceptions
37
58
  require 'raven/integrations/rails/overrides/debug_exceptions_catcher'
38
59
  if defined?(::ActionDispatch::DebugExceptions)
@@ -40,7 +61,18 @@ module Raven
40
61
  elsif defined?(::ActionDispatch::ShowExceptions)
41
62
  exceptions_class = ::ActionDispatch::ShowExceptions
42
63
  end
43
- Raven.rails_safely_prepend("DebugExceptionsCatcher", :to => exceptions_class)
64
+
65
+ Raven.safely_prepend(
66
+ "DebugExceptionsCatcher",
67
+ :from => Raven::Rails::Overrides,
68
+ :to => exceptions_class
69
+ )
70
+ end
71
+ end
72
+
73
+ initializer 'raven.active_job' do
74
+ ActiveSupport.on_load :active_job do
75
+ require 'raven/integrations/rails/active_job'
44
76
  end
45
77
  end
46
78
 
@@ -4,9 +4,14 @@ require 'raven/integrations/tasks'
4
4
 
5
5
  module Rake
6
6
  class Application
7
- alias :orig_display_error_messsage :display_error_message
7
+ alias orig_display_error_messsage display_error_message
8
8
  def display_error_message(ex)
9
- Raven.capture_exception ex, :logger => 'rake', :tags => { 'rake_task' => @name }
9
+ Raven.capture_exception(
10
+ ex,
11
+ :transaction => top_level_tasks.join(' '),
12
+ :logger => 'rake',
13
+ :tags => { 'rake_task' => top_level_tasks.join(' ') }
14
+ )
10
15
  orig_display_error_messsage(ex)
11
16
  end
12
17
  end
@@ -0,0 +1,13 @@
1
+ module Raven
2
+ module Sidekiq
3
+ class CleanupMiddleware
4
+ def call(_worker, job, queue)
5
+ Raven.context.transaction.push "Sidekiq/#{job['class']}"
6
+ Raven.extra_context(:sidekiq => job.merge("queue" => queue))
7
+ yield
8
+ Context.clear!
9
+ BreadcrumbBuffer.clear!
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,38 @@
1
+ require 'raven/utils/context_filter'
2
+
3
+ module Raven
4
+ module Sidekiq
5
+ class ErrorHandler
6
+ SIDEKIQ_NAME = "Sidekiq".freeze
7
+
8
+ def call(ex, context)
9
+ context = Utils::ContextFilter.filter_context(context)
10
+ Raven.context.transaction.push transaction_from_context(context)
11
+ Raven.capture_exception(
12
+ ex,
13
+ :message => ex.message,
14
+ :extra => { :sidekiq => context }
15
+ )
16
+ Context.clear!
17
+ BreadcrumbBuffer.clear!
18
+ end
19
+
20
+ private
21
+
22
+ # this will change in the future:
23
+ # https://github.com/mperham/sidekiq/pull/3161
24
+ def transaction_from_context(context)
25
+ classname = (context["wrapped"] || context["class"] ||
26
+ (context[:job] && (context[:job]["wrapped"] || context[:job]["class"]))
27
+ )
28
+ if classname
29
+ "#{SIDEKIQ_NAME}/#{classname}"
30
+ elsif context[:event]
31
+ "#{SIDEKIQ_NAME}/#{context[:event]}"
32
+ else
33
+ SIDEKIQ_NAME
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -1,55 +1,13 @@
1
1
  require 'time'
2
2
  require 'sidekiq'
3
+ require 'raven/integrations/sidekiq/cleanup_middleware'
4
+ require 'raven/integrations/sidekiq/error_handler'
3
5
 
4
- module Raven
5
- class Sidekiq
6
- def call(_worker, msg, _queue)
7
- started_at = Time.now
8
- yield
9
- rescue Exception => ex
10
- Raven.capture_exception(ex, :extra => { :sidekiq => msg },
11
- :time_spent => Time.now-started_at)
12
- raise
13
- end
14
- end
15
- end
16
-
17
- if Sidekiq::VERSION < '3'
18
- # old behavior
19
- ::Sidekiq.configure_server do |config|
20
- config.server_middleware do |chain|
21
- chain.add ::Raven::Sidekiq
22
- end
23
- end
24
- else
6
+ if Sidekiq::VERSION > '3'
25
7
  Sidekiq.configure_server do |config|
26
- config.error_handlers << Proc.new do |ex, context|
27
- Raven.capture_exception(ex, :extra => {
28
- :sidekiq => filter_context(context)
29
- })
8
+ config.error_handlers << Raven::Sidekiq::ErrorHandler.new
9
+ config.server_middleware do |chain|
10
+ chain.add Raven::Sidekiq::CleanupMiddleware
30
11
  end
31
12
  end
32
13
  end
33
-
34
- def filter_context(context)
35
- case context
36
- when Array
37
- context.map { |arg| filter_context(arg) }
38
- when Hash
39
- Hash[context.map { |key, value| filter_context_hash(key, value) }]
40
- else
41
- context
42
- end
43
- end
44
-
45
- def filter_context_hash(key, value)
46
- # Strip any `_aj` prefixes from keys.
47
- # These keys come from an internal serialized object from ActiveJob.
48
- # Internally, there are a subset of keys that ActiveJob references, but
49
- # these are declared as private, and I don't think it's wise
50
- # to keep chasing what this list is. But they all use a common prefix, so
51
- # we want to strip this becuase ActiveJob will complain.
52
- # e.g.: _aj_globalid -> _globalid
53
- (key = key[3..-1]) if key [0..3] == "_aj_"
54
- [key, filter_context(value)]
55
- end
@@ -4,7 +4,7 @@ require 'raven/cli'
4
4
  namespace :raven do
5
5
  desc "Send a test event to the remote Sentry server"
6
6
  task :test, [:dsn] do |_t, args|
7
- Rake::Task["environment"].invoke if defined? Rails
7
+ Rake::Task["environment"].invoke if Rake::Task.tasks.map(&:to_s).include?("environment")
8
8
 
9
9
  Raven::CLI.test(args.dsn)
10
10
  end
@@ -0,0 +1,25 @@
1
+ module Raven
2
+ class Interface
3
+ def initialize(attributes = nil)
4
+ attributes&.each do |attr, value|
5
+ public_send "#{attr}=", value
6
+ end
7
+
8
+ yield self if block_given?
9
+ end
10
+
11
+ def self.inherited(klass)
12
+ name = klass.name.split("::").last.downcase.gsub("interface", "")
13
+ registered[name.to_sym] = klass
14
+ super
15
+ end
16
+
17
+ def self.registered
18
+ @@registered ||= {} # rubocop:disable Style/ClassVars
19
+ end
20
+
21
+ def to_hash
22
+ Hash[instance_variables.map { |name| [name[1..-1].to_sym, instance_variable_get(name)] }]
23
+ end
24
+ end
25
+ end
@@ -1,18 +1,15 @@
1
- require 'raven/interfaces'
2
-
3
1
  module Raven
4
2
  class ExceptionInterface < Interface
5
- name 'exception'
6
3
  attr_accessor :values
7
4
 
5
+ def self.sentry_alias
6
+ :exception
7
+ end
8
+
8
9
  def to_hash(*args)
9
10
  data = super(*args)
10
- if data[:values]
11
- data[:values] = data[:values].map(&:to_hash)
12
- end
11
+ data[:values] = data[:values].map(&:to_hash) if data[:values]
13
12
  data
14
13
  end
15
14
  end
16
-
17
- register_interface :exception => ExceptionInterface
18
15
  end
@@ -1,15 +1,6 @@
1
- require 'raven/interfaces'
2
-
3
1
  module Raven
4
2
  class HttpInterface < Interface
5
- name 'request'
6
- attr_accessor :url
7
- attr_accessor :method
8
- attr_accessor :data
9
- attr_accessor :query_string
10
- attr_accessor :cookies
11
- attr_accessor :headers
12
- attr_accessor :env
3
+ attr_accessor :url, :method, :data, :query_string, :cookies, :headers, :env
13
4
 
14
5
  def initialize(*arguments)
15
6
  self.headers = {}
@@ -17,7 +8,9 @@ module Raven
17
8
  self.cookies = nil
18
9
  super(*arguments)
19
10
  end
20
- end
21
11
 
22
- register_interface :http => HttpInterface
12
+ def self.sentry_alias
13
+ :request
14
+ end
15
+ end
23
16
  end
@@ -1,16 +1,20 @@
1
- require 'raven/interfaces'
1
+ require 'raven/interface'
2
2
 
3
3
  module Raven
4
4
  class MessageInterface < Interface
5
- name 'sentry.interfaces.Message'
6
- attr_accessor :message
7
- attr_accessor :params
5
+ attr_accessor :message, :params
8
6
 
9
7
  def initialize(*arguments)
10
8
  self.params = []
11
9
  super(*arguments)
12
10
  end
13
- end
14
11
 
15
- register_interface :message => MessageInterface
12
+ def unformatted_message
13
+ Array(params).empty? ? message : message % params
14
+ end
15
+
16
+ def self.sentry_alias
17
+ :logentry
18
+ end
19
+ end
16
20
  end
@@ -1,5 +1,3 @@
1
- require 'raven/interfaces'
2
-
3
1
  module Raven
4
2
  class SingleExceptionInterface < Interface
5
3
  attr_accessor :type
@@ -9,9 +7,7 @@ module Raven
9
7
 
10
8
  def to_hash(*args)
11
9
  data = super(*args)
12
- if data[:stacktrace]
13
- data[:stacktrace] = data[:stacktrace].to_hash
14
- end
10
+ data[:stacktrace] = data[:stacktrace].to_hash if data[:stacktrace]
15
11
  data
16
12
  end
17
13
  end
@@ -1,15 +1,15 @@
1
- require 'raven/interfaces'
2
-
3
1
  module Raven
4
2
  class StacktraceInterface < Interface
5
- name 'stacktrace'
6
3
  attr_accessor :frames
7
4
 
8
5
  def initialize(*arguments)
9
- self.frames = []
10
6
  super(*arguments)
11
7
  end
12
8
 
9
+ def self.sentry_alias
10
+ :stacktrace
11
+ end
12
+
13
13
  def to_hash(*args)
14
14
  data = super(*args)
15
15
  data[:frames] = data[:frames].map(&:to_hash)
@@ -18,23 +18,16 @@ module Raven
18
18
 
19
19
  # Not actually an interface, but I want to use the same style
20
20
  class Frame < Interface
21
- attr_accessor :abs_path
22
- attr_accessor :function
23
- attr_accessor :vars
24
- attr_accessor :pre_context
25
- attr_accessor :post_context
26
- attr_accessor :context_line
27
- attr_accessor :module
28
- attr_accessor :lineno
29
- attr_accessor :in_app
21
+ attr_accessor :abs_path, :context_line, :function, :in_app,
22
+ :lineno, :module, :pre_context, :post_context, :vars
30
23
 
31
24
  def initialize(*arguments)
32
- self.vars, self.pre_context, self.post_context = [], [], []
33
25
  super(*arguments)
34
26
  end
35
27
 
36
28
  def filename
37
- return nil if self.abs_path.nil?
29
+ return if abs_path.nil?
30
+ return @filename if instance_variable_defined?(:@filename)
38
31
 
39
32
  prefix =
40
33
  if under_project_root? && in_app
@@ -45,32 +38,32 @@ module Raven
45
38
  longest_load_path
46
39
  end
47
40
 
48
- prefix ? self.abs_path[prefix.to_s.chomp(File::SEPARATOR).length+1..-1] : self.abs_path
41
+ @filename = prefix ? abs_path[prefix.to_s.chomp(File::SEPARATOR).length + 1..-1] : abs_path
42
+ end
43
+
44
+ def to_hash(*args)
45
+ data = super(*args)
46
+ data[:filename] = filename
47
+ data.delete(:vars) unless vars && !vars.empty?
48
+ data.delete(:pre_context) unless pre_context && !pre_context.empty?
49
+ data.delete(:post_context) unless post_context && !post_context.empty?
50
+ data.delete(:context_line) unless context_line && !context_line.empty?
51
+ data
49
52
  end
50
53
 
54
+ private
55
+
51
56
  def under_project_root?
52
57
  project_root && abs_path.start_with?(project_root)
53
58
  end
54
59
 
55
60
  def project_root
56
- @project_root ||= Raven.configuration.project_root && Raven.configuration.project_root.to_s
61
+ @project_root ||= Raven.configuration.project_root&.to_s
57
62
  end
58
63
 
59
64
  def longest_load_path
60
- $LOAD_PATH.select { |s| self.abs_path.start_with?(s.to_s) }.sort_by { |s| s.to_s.length }.last
61
- end
62
-
63
- def to_hash(*args)
64
- data = super(*args)
65
- data[:filename] = self.filename
66
- data.delete(:vars) unless self.vars && !self.vars.empty?
67
- data.delete(:pre_context) unless self.pre_context && !self.pre_context.empty?
68
- data.delete(:post_context) unless self.post_context && !self.post_context.empty?
69
- data.delete(:context_line) unless self.context_line && !self.context_line.empty?
70
- data
65
+ $LOAD_PATH.select { |path| abs_path.start_with?(path.to_s) }.max_by(&:size)
71
66
  end
72
67
  end
73
68
  end
74
-
75
- register_interface :stack_trace => StacktraceInterface
76
69
  end
@@ -1,32 +1,44 @@
1
- # A much simpler source line cacher because linecache sucks at platform compat
2
-
3
1
  module Raven
4
2
  class LineCache
5
- class << self
6
- # TODO: a constant isn't appropriate here, refactor
7
- # also would there be threading bugs essentially using this as a class
8
- # variable?
9
- CACHE = {} # rubocop:disable Style/MutableConstant
10
-
11
- def is_valid_file(path)
12
- lines = getlines(path)
13
- !lines.nil?
14
- end
3
+ def initialize
4
+ @cache = {}
5
+ end
15
6
 
16
- def getlines(path)
17
- CACHE[path] ||= begin
18
- IO.readlines(path)
19
- rescue
20
- nil
21
- end
7
+ # Any linecache you provide to Raven must implement this method.
8
+ # Returns an Array of Strings representing the lines in the source
9
+ # file. The number of lines retrieved is (2 * context) + 1, the middle
10
+ # line should be the line requested by lineno. See specs for more information.
11
+ def get_file_context(filename, lineno, context)
12
+ return nil, nil, nil unless valid_path?(filename)
13
+
14
+ lines = Array.new(2 * context + 1) do |i|
15
+ getline(filename, lineno - context + i)
22
16
  end
17
+ [lines[0..(context - 1)], lines[context], lines[(context + 1)..-1]]
18
+ end
19
+
20
+ private
23
21
 
24
- def getline(path, n)
25
- return nil if n < 1
26
- lines = getlines(path)
27
- return nil if lines.nil?
28
- lines[n - 1]
22
+ def valid_path?(path)
23
+ lines = getlines(path)
24
+ !lines.nil?
25
+ end
26
+
27
+ def getlines(path)
28
+ @cache[path] ||= begin
29
+ IO.readlines(path)
30
+ rescue
31
+ nil
29
32
  end
30
33
  end
34
+
35
+ def getline(path, n)
36
+ return nil if n < 1
37
+
38
+ lines = getlines(path)
39
+ return nil if lines.nil?
40
+
41
+ lines[n - 1]
42
+ end
31
43
  end
32
44
  end
data/lib/raven/logger.rb CHANGED
@@ -1,22 +1,19 @@
1
1
  # frozen_string_literal: true
2
- module Raven
3
- class Logger
4
- LOG_PREFIX = "** [Raven] ".freeze
5
2
 
6
- [
7
- :fatal,
8
- :error,
9
- :warn,
10
- :info,
11
- :debug,
12
- ].each do |level|
13
- define_method level do |*args, &block|
14
- msg = args[0] # Block-level default args is a 1.9 feature
15
- msg ||= block.call if block
16
- logger = Raven.configuration[:logger]
17
- logger = ::Logger.new(STDOUT) if logger.nil?
3
+ require 'logger'
4
+
5
+ module Raven
6
+ class Logger < ::Logger
7
+ LOG_PREFIX = "** [Raven] "
8
+ PROGNAME = "sentry"
18
9
 
19
- logger.send(level, "#{LOG_PREFIX}#{msg}") if logger
10
+ def initialize(*)
11
+ super
12
+ @level = ::Logger::INFO
13
+ original_formatter = ::Logger::Formatter.new
14
+ @default_formatter = proc do |severity, datetime, _progname, msg|
15
+ msg = "#{LOG_PREFIX}#{msg}"
16
+ original_formatter.call(severity, datetime, PROGNAME, msg)
20
17
  end
21
18
  end
22
19
  end
@@ -1,16 +1,36 @@
1
1
  module Raven
2
2
  class Processor::Cookies < Processor
3
3
  def process(data)
4
- if data[:request]
5
- # Remove possibly sensitive cookies
6
- data[:request][:cookies] = nil if data[:request][:cookies]
4
+ process_if_symbol_keys(data) if data[:request]
5
+ process_if_string_keys(data) if data["request"]
7
6
 
8
- if data[:request][:headers] && data[:request][:headers]["Cookie"]
9
- data[:request][:headers]["Cookie"] = nil
10
- end
7
+ data
8
+ end
9
+
10
+ private
11
+
12
+ def process_if_symbol_keys(data)
13
+ if cookies = data.dig(:request, :cookies)
14
+ data[:request][:cookies] = generate_masked_cookies(cookies)
11
15
  end
12
16
 
13
- data
17
+ if cookies_header = data[:request][:headers]["Cookie"]
18
+ data[:request][:headers]["Cookie"] = generate_masked_cookies(cookies_header)
19
+ end
20
+ end
21
+
22
+ def process_if_string_keys(data)
23
+ if cookies = data.dig("request", "cookies")
24
+ data["request"]["cookies"] = generate_masked_cookies(cookies)
25
+ end
26
+
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 } if cookies.respond_to?(:merge)
14
34
  end
15
35
  end
16
36
  end
@@ -0,0 +1,55 @@
1
+ module Raven
2
+ class Processor::HTTPHeaders < Processor
3
+ DEFAULT_FIELDS = ["Authorization"].freeze
4
+
5
+ attr_accessor :sanitize_http_headers
6
+
7
+ def initialize(client)
8
+ super
9
+ self.sanitize_http_headers = client.configuration.sanitize_http_headers
10
+ end
11
+
12
+ def process(data)
13
+ process_if_symbol_keys(data) if data[:request]
14
+ process_if_string_keys(data) if data["request"]
15
+
16
+ data
17
+ end
18
+
19
+ private
20
+
21
+ def process_if_symbol_keys(data)
22
+ return unless data[:request][:headers]
23
+
24
+ data[:request][:headers].keys.select { |k| fields_re.match(k.to_s) }.each do |k|
25
+ data[:request][:headers][k] = STRING_MASK
26
+ end
27
+ end
28
+
29
+ def process_if_string_keys(data)
30
+ return unless data["request"]["headers"]
31
+
32
+ data["request"]["headers"].keys.select { |k| fields_re.match(k) }.each do |k|
33
+ data["request"]["headers"][k] = STRING_MASK
34
+ end
35
+ end
36
+
37
+ def matches_regexes?(k)
38
+ fields_re.match(k.to_s)
39
+ end
40
+
41
+ def fields_re
42
+ @fields_re ||= /#{(DEFAULT_FIELDS | sanitize_http_headers).map do |f|
43
+ use_boundary?(f) ? "\\b#{f}\\b" : f
44
+ end.join("|")}/i
45
+ end
46
+
47
+ def use_boundary?(string)
48
+ !DEFAULT_FIELDS.include?(string) && !special_characters?(string)
49
+ end
50
+
51
+ def special_characters?(string)
52
+ REGEX_SPECIAL_CHARACTERS.select { |r| string.include?(r) }.any?
53
+ end
54
+ end
55
+ end
@@ -1,11 +1,24 @@
1
1
  module Raven
2
2
  class Processor::PostData < Processor
3
3
  def process(data)
4
- if data[:request] && data[:request][:method] == "POST"
5
- data[:request][:data] = nil # Remove possibly sensitive POST data
6
- end
4
+ process_if_symbol_keys(data) if data[:request]
5
+ process_if_string_keys(data) if data["request"]
7
6
 
8
7
  data
9
8
  end
9
+
10
+ private
11
+
12
+ def process_if_symbol_keys(data)
13
+ return unless data[:request][:method] == "POST"
14
+
15
+ data[:request][:data] = STRING_MASK
16
+ end
17
+
18
+ def process_if_string_keys(data)
19
+ return unless data["request"]["method"] == "POST"
20
+
21
+ data["request"]["data"] = STRING_MASK
22
+ end
10
23
  end
11
24
  end