rollbar 2.8.3 → 3.6.0

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 (238) hide show
  1. checksums.yaml +5 -5
  2. data/.codeclimate.yml +18 -0
  3. data/.github/pull_request_template.md +34 -0
  4. data/.github/workflows/ci.yml +67 -0
  5. data/.gitignore +3 -1
  6. data/.rubocop.yml +206 -7
  7. data/Appraisals +10 -10
  8. data/CHANGELOG.md +257 -3
  9. data/Gemfile +74 -13
  10. data/README.md +38 -833
  11. data/Rakefile +0 -0
  12. data/THANKS.md +1 -0
  13. data/data/rollbar.snippet.js +1 -1
  14. data/docs/configuration.md +64 -3
  15. data/docs/plugins.md +46 -0
  16. data/gemfiles/rails50.gemfile +56 -0
  17. data/gemfiles/rails51.gemfile +57 -0
  18. data/gemfiles/rails52.gemfile +56 -0
  19. data/gemfiles/rails60.gemfile +52 -0
  20. data/gemfiles/rails61.gemfile +52 -0
  21. data/gemfiles/rails70.gemfile +52 -0
  22. data/gemfiles/rails71.gemfile +52 -0
  23. data/lib/generators/rollbar/rollbar_generator.rb +24 -20
  24. data/lib/generators/rollbar/templates/{initializer.rb → initializer.erb} +19 -5
  25. data/lib/rails/rollbar_runner.rb +26 -22
  26. data/lib/rollbar/capistrano.rb +78 -38
  27. data/lib/rollbar/capistrano3.rb +62 -1
  28. data/lib/rollbar/capistrano_tasks.rb +166 -0
  29. data/lib/rollbar/configuration.rb +291 -71
  30. data/lib/rollbar/delay/active_job.rb +17 -0
  31. data/lib/rollbar/delay/delayed_job.rb +23 -0
  32. data/lib/rollbar/delay/girl_friday.rb +4 -9
  33. data/lib/rollbar/delay/resque.rb +3 -6
  34. data/lib/rollbar/delay/shoryuken.rb +36 -0
  35. data/lib/rollbar/delay/sidekiq.rb +6 -8
  36. data/lib/rollbar/delay/sucker_punch.rb +17 -22
  37. data/lib/rollbar/delay/thread.rb +74 -3
  38. data/lib/rollbar/deploy.rb +91 -0
  39. data/lib/rollbar/encoding/encoder.rb +22 -11
  40. data/lib/rollbar/encoding.rb +2 -7
  41. data/lib/rollbar/exception_reporter.rb +36 -12
  42. data/lib/rollbar/item/backtrace.rb +118 -0
  43. data/lib/rollbar/item/frame.rb +121 -0
  44. data/lib/rollbar/item/locals.rb +103 -0
  45. data/lib/rollbar/item.rb +314 -0
  46. data/lib/rollbar/js.rb +0 -28
  47. data/lib/rollbar/json.rb +7 -55
  48. data/lib/rollbar/language_support.rb +7 -19
  49. data/lib/rollbar/lazy_store.rb +8 -12
  50. data/lib/rollbar/logger.rb +71 -0
  51. data/lib/rollbar/logger_proxy.rb +18 -1
  52. data/lib/rollbar/middleware/js/json_value.rb +36 -0
  53. data/lib/rollbar/middleware/js.rb +297 -0
  54. data/lib/rollbar/middleware/rack/builder.rb +4 -4
  55. data/lib/rollbar/middleware/rack/test_session.rb +4 -4
  56. data/lib/rollbar/middleware/rack.rb +52 -0
  57. data/lib/rollbar/middleware/rails/rollbar.rb +19 -7
  58. data/lib/rollbar/middleware/rails/show_exceptions.rb +21 -9
  59. data/lib/rollbar/middleware/sinatra.rb +2 -40
  60. data/lib/rollbar/notifier/trace_with_bindings.rb +75 -0
  61. data/lib/rollbar/notifier.rb +913 -0
  62. data/lib/rollbar/plugin.rb +126 -0
  63. data/lib/rollbar/plugins/active_job.rb +54 -0
  64. data/lib/rollbar/plugins/basic_socket.rb +31 -0
  65. data/lib/rollbar/plugins/delayed_job/job_data.rb +50 -0
  66. data/lib/rollbar/plugins/delayed_job/plugin.rb +88 -0
  67. data/lib/rollbar/plugins/delayed_job.rb +12 -0
  68. data/lib/rollbar/plugins/error_context.rb +11 -0
  69. data/lib/rollbar/plugins/goalie.rb +65 -0
  70. data/lib/rollbar/plugins/rack.rb +18 -0
  71. data/lib/rollbar/plugins/rails/controller_methods.rb +56 -0
  72. data/lib/rollbar/plugins/rails/error_subscriber.rb +12 -0
  73. data/lib/rollbar/plugins/rails/railtie30.rb +18 -0
  74. data/lib/rollbar/plugins/rails/railtie32.rb +18 -0
  75. data/lib/rollbar/plugins/rails/railtie_mixin.rb +37 -0
  76. data/lib/rollbar/plugins/rails.rb +89 -0
  77. data/lib/rollbar/plugins/rake.rb +73 -0
  78. data/lib/rollbar/plugins/resque/failure.rb +39 -0
  79. data/lib/rollbar/plugins/resque.rb +11 -0
  80. data/lib/rollbar/plugins/sidekiq/plugin.rb +77 -0
  81. data/lib/rollbar/plugins/sidekiq.rb +37 -0
  82. data/lib/rollbar/plugins/thread.rb +14 -0
  83. data/lib/rollbar/plugins/validations.rb +45 -0
  84. data/lib/rollbar/plugins.rb +47 -0
  85. data/lib/rollbar/rails.rb +0 -1
  86. data/lib/rollbar/rake_tasks.rb +4 -66
  87. data/lib/rollbar/request_data_extractor.rb +157 -117
  88. data/lib/rollbar/rollbar_test.rb +38 -0
  89. data/lib/rollbar/scrubbers/params.rb +133 -0
  90. data/lib/rollbar/scrubbers/url.rb +90 -35
  91. data/lib/rollbar/scrubbers.rb +13 -0
  92. data/lib/rollbar/truncation/frames_strategy.rb +2 -1
  93. data/lib/rollbar/truncation/min_body_strategy.rb +3 -4
  94. data/lib/rollbar/truncation/mixin.rb +1 -1
  95. data/lib/rollbar/truncation/remove_any_key_strategy.rb +126 -0
  96. data/lib/rollbar/truncation/remove_extra_strategy.rb +37 -0
  97. data/lib/rollbar/truncation/remove_request_strategy.rb +21 -0
  98. data/lib/rollbar/truncation/strings_strategy.rb +6 -5
  99. data/lib/rollbar/truncation.rb +10 -4
  100. data/lib/rollbar/util/hash.rb +37 -6
  101. data/lib/rollbar/util/ip_anonymizer.rb +33 -0
  102. data/lib/rollbar/util/ip_obfuscator.rb +1 -1
  103. data/lib/rollbar/util.rb +101 -55
  104. data/lib/rollbar/version.rb +1 -1
  105. data/lib/rollbar.rb +91 -879
  106. data/lib/tasks/benchmark.rake +104 -0
  107. data/lib/tasks/tasks.rake +3 -3
  108. data/rollbar.gemspec +21 -32
  109. data/spec/support/rollbar_api.rb +67 -0
  110. metadata +78 -439
  111. data/.travis.yml +0 -155
  112. data/gemfiles/rails30.gemfile +0 -20
  113. data/gemfiles/rails31.gemfile +0 -16
  114. data/gemfiles/rails32.gemfile +0 -17
  115. data/gemfiles/rails40.gemfile +0 -17
  116. data/gemfiles/rails41.gemfile +0 -15
  117. data/gemfiles/rails42.gemfile +0 -15
  118. data/lib/rollbar/active_job.rb +0 -11
  119. data/lib/rollbar/active_record_extension.rb +0 -14
  120. data/lib/rollbar/core_ext/basic_socket.rb +0 -7
  121. data/lib/rollbar/core_ext/thread.rb +0 -9
  122. data/lib/rollbar/delayed_job.rb +0 -78
  123. data/lib/rollbar/encoding/legacy_encoder.rb +0 -20
  124. data/lib/rollbar/goalie.rb +0 -33
  125. data/lib/rollbar/js/frameworks/rails.rb +0 -29
  126. data/lib/rollbar/js/frameworks.rb +0 -6
  127. data/lib/rollbar/js/middleware.rb +0 -129
  128. data/lib/rollbar/js/version.rb +0 -5
  129. data/lib/rollbar/json/default.rb +0 -11
  130. data/lib/rollbar/json/oj.rb +0 -15
  131. data/lib/rollbar/rack.rb +0 -9
  132. data/lib/rollbar/rails/controller_methods.rb +0 -40
  133. data/lib/rollbar/railtie.rb +0 -46
  134. data/lib/rollbar/rake.rb +0 -38
  135. data/lib/rollbar/sidekiq.rb +0 -40
  136. data/lib/rollbar/tasks/rollbar.cap +0 -45
  137. data/spec/cacert.pem +0 -3988
  138. data/spec/controllers/home_controller_spec.rb +0 -455
  139. data/spec/delay/sidekiq_spec.rb +0 -61
  140. data/spec/delay/sucker_punch_spec.rb +0 -25
  141. data/spec/delayed/backend/test.rb +0 -139
  142. data/spec/delayed/serialization/test.rb +0 -0
  143. data/spec/dummyapp/.gitignore +0 -73
  144. data/spec/dummyapp/Rakefile +0 -7
  145. data/spec/dummyapp/app/assets/javascripts/application.js +0 -3
  146. data/spec/dummyapp/app/assets/stylesheets/application.css.scss +0 -37
  147. data/spec/dummyapp/app/controllers/application_controller.rb +0 -3
  148. data/spec/dummyapp/app/controllers/home_controller.rb +0 -60
  149. data/spec/dummyapp/app/controllers/users_controller.rb +0 -17
  150. data/spec/dummyapp/app/helpers/.gitkeep +0 -0
  151. data/spec/dummyapp/app/mailers/.gitkeep +0 -0
  152. data/spec/dummyapp/app/models/.gitkeep +0 -0
  153. data/spec/dummyapp/app/models/user.rb +0 -7
  154. data/spec/dummyapp/app/views/devise/registrations/edit.html.erb +0 -27
  155. data/spec/dummyapp/app/views/devise/registrations/new.html.erb +0 -20
  156. data/spec/dummyapp/app/views/devise/shared/_links.html.erb +0 -25
  157. data/spec/dummyapp/app/views/home/cause_exception.html.erb +0 -1
  158. data/spec/dummyapp/app/views/home/index.html.erb +0 -4
  159. data/spec/dummyapp/app/views/home/report_exception.html.erb +0 -1
  160. data/spec/dummyapp/app/views/js/test.html.erb +0 -1
  161. data/spec/dummyapp/app/views/layouts/_messages.html.erb +0 -5
  162. data/spec/dummyapp/app/views/layouts/_navigation.html.erb +0 -21
  163. data/spec/dummyapp/app/views/layouts/application.html.erb +0 -25
  164. data/spec/dummyapp/app/views/layouts/simple.html.erb +0 -18
  165. data/spec/dummyapp/app/views/users/index.html.erb +0 -8
  166. data/spec/dummyapp/app/views/users/show.html.erb +0 -3
  167. data/spec/dummyapp/config/application.rb +0 -59
  168. data/spec/dummyapp/config/boot.rb +0 -10
  169. data/spec/dummyapp/config/database.yml +0 -25
  170. data/spec/dummyapp/config/environment.rb +0 -5
  171. data/spec/dummyapp/config/environments/development.rb +0 -37
  172. data/spec/dummyapp/config/environments/production.rb +0 -67
  173. data/spec/dummyapp/config/environments/test.rb +0 -37
  174. data/spec/dummyapp/config/initializers/backtrace_silencers.rb +0 -7
  175. data/spec/dummyapp/config/initializers/inflections.rb +0 -15
  176. data/spec/dummyapp/config/initializers/mime_types.rb +0 -5
  177. data/spec/dummyapp/config/initializers/rollbar.rb +0 -23
  178. data/spec/dummyapp/config/initializers/secret_token.rb +0 -7
  179. data/spec/dummyapp/config/initializers/session_store.rb +0 -8
  180. data/spec/dummyapp/config/initializers/wrap_parameters.rb +0 -16
  181. data/spec/dummyapp/config/locales/devise.en.yml +0 -58
  182. data/spec/dummyapp/config/locales/en.yml +0 -5
  183. data/spec/dummyapp/config/routes.rb +0 -17
  184. data/spec/dummyapp/config.ru +0 -4
  185. data/spec/dummyapp/db/migrate/20121121184652_devise_create_users.rb +0 -46
  186. data/spec/dummyapp/db/migrate/20121121184654_add_name_to_users.rb +0 -5
  187. data/spec/dummyapp/db/schema.rb +0 -35
  188. data/spec/dummyapp/db/seeds.rb +0 -12
  189. data/spec/dummyapp/lib/assets/.gitkeep +0 -0
  190. data/spec/dummyapp/public/404.html +0 -26
  191. data/spec/dummyapp/public/422.html +0 -26
  192. data/spec/dummyapp/public/500.html +0 -25
  193. data/spec/dummyapp/public/favicon.ico +0 -0
  194. data/spec/dummyapp/script/rails +0 -6
  195. data/spec/fixtures/file1 +0 -1
  196. data/spec/fixtures/file2 +0 -1
  197. data/spec/fixtures/payloads/message.json +0 -25
  198. data/spec/fixtures/payloads/sample.trace.json +0 -275
  199. data/spec/fixtures/payloads/sample.trace_chain.json +0 -530
  200. data/spec/generators/rollbar/rollbar_generator_spec.rb +0 -24
  201. data/spec/requests/home_spec.rb +0 -49
  202. data/spec/rollbar/active_job_spec.rb +0 -33
  203. data/spec/rollbar/configuration_spec.rb +0 -24
  204. data/spec/rollbar/delay/girl_friday_spec.rb +0 -41
  205. data/spec/rollbar/delay/resque_spec.rb +0 -37
  206. data/spec/rollbar/delay/thread_spec.rb +0 -27
  207. data/spec/rollbar/delayed_job/job_data.rb +0 -35
  208. data/spec/rollbar/delayed_job_spec.rb +0 -90
  209. data/spec/rollbar/encoding/encoder_spec.rb +0 -63
  210. data/spec/rollbar/js/frameworks/rails_spec.rb +0 -19
  211. data/spec/rollbar/js/middleware_spec.rb +0 -162
  212. data/spec/rollbar/json/oj_spec.rb +0 -18
  213. data/spec/rollbar/json_spec.rb +0 -110
  214. data/spec/rollbar/lazy_store_spec.rb +0 -99
  215. data/spec/rollbar/logger_proxy_spec.rb +0 -34
  216. data/spec/rollbar/middleware/rack/builder_spec.rb +0 -151
  217. data/spec/rollbar/middleware/sinatra_spec.rb +0 -197
  218. data/spec/rollbar/rake_spec.rb +0 -34
  219. data/spec/rollbar/request_data_extractor_spec.rb +0 -82
  220. data/spec/rollbar/scrubbers/url_spec.rb +0 -111
  221. data/spec/rollbar/sidekiq_spec.rb +0 -90
  222. data/spec/rollbar/truncation/frames_strategy_spec.rb +0 -70
  223. data/spec/rollbar/truncation/min_body_strategy_spec.rb +0 -57
  224. data/spec/rollbar/truncation/strings_strategy_spec.rb +0 -89
  225. data/spec/rollbar/truncation_spec.rb +0 -27
  226. data/spec/rollbar/util/hash_spec.rb +0 -22
  227. data/spec/rollbar/util_spec.rb +0 -19
  228. data/spec/rollbar_bc_spec.rb +0 -380
  229. data/spec/rollbar_spec.rb +0 -2067
  230. data/spec/spec_helper.rb +0 -49
  231. data/spec/support/cause_exception.rb +0 -1
  232. data/spec/support/encoding_helpers.rb +0 -8
  233. data/spec/support/encodings/iso_8859_9 +0 -1
  234. data/spec/support/fixture_helpers.rb +0 -10
  235. data/spec/support/get_ip_raising.rb +0 -7
  236. data/spec/support/helpers.rb +0 -5
  237. data/spec/support/notifier_helpers.rb +0 -36
  238. data/spec/support/shared_contexts.rb +0 -12
@@ -6,34 +6,66 @@ require 'rollbar/language_support'
6
6
  module Rollbar
7
7
  module Scrubbers
8
8
  class URL
9
- attr_reader :regex
10
- attr_reader :scrub_user
11
- attr_reader :scrub_password
12
- attr_reader :randomize_scrub_length
9
+ SCRUB_ALL = :scrub_all
13
10
 
14
- def initialize(options = {})
15
- @regex = build_regex(options[:scrub_fields])
16
- @scrub_user = options[:scrub_user]
17
- @scrub_password = options[:scrub_password]
18
- @randomize_scrub_length = options.fetch(:randomize_scrub_length, true)
11
+ def self.call(*args)
12
+ new.call(*args)
19
13
  end
20
14
 
21
- def call(url)
22
- return url unless Rollbar::LanguageSupport.can_scrub_url?
15
+ def call(options = {})
16
+ url = ascii_encode(options[:url])
17
+
18
+ filter(url,
19
+ build_regex(options[:scrub_fields]),
20
+ options[:scrub_user],
21
+ options[:scrub_password],
22
+ options.fetch(:randomize_scrub_length, true),
23
+ options[:scrub_fields].include?(SCRUB_ALL),
24
+ build_whitelist_regex(options[:whitelist] || []))
25
+ rescue StandardError => e
26
+ message = '[Rollbar] There was an error scrubbing the url: ' \
27
+ "#{e}, options: #{options.inspect}"
28
+ Rollbar.logger.error(message)
29
+ url
30
+ end
31
+
32
+ private
33
+
34
+ def ascii_encode(url)
35
+ # In some cases non-ascii characters won't be properly encoded, so we do it here.
36
+ #
37
+ # The standard encoders (the CGI and URI methods) are not reliable when
38
+ # the query string is already embedded in the full URL, but the inconsistencies
39
+ # are limited to issues with characters in the ascii range. (For example,
40
+ # the '#' if it appears in an unexpected place.) For escaping non-ascii,
41
+ # they are all OK, so we'll take care to skip the ascii chars.
42
+
43
+ return url if url.ascii_only?
44
+
45
+ # Iterate each char and only escape non-ascii characters.
46
+ url.each_char.map { |c| c.ascii_only? ? c : CGI.escape(c) }.join
47
+ end
48
+
49
+ def build_whitelist_regex(whitelist)
50
+ fields = whitelist.find_all { |f| f.is_a?(String) || f.is_a?(Symbol) }
51
+ return unless fields.any?
52
+
53
+ Regexp.new(fields.map { |val| /\A#{Regexp.escape(val.to_s)}\z/ }.join('|'))
54
+ end
23
55
 
56
+ def filter(url, regex, scrub_user, scrub_password, randomize_scrub_length,
57
+ scrub_all, whitelist)
24
58
  uri = URI.parse(url)
25
59
 
26
- uri.user = filter_user(uri.user)
27
- uri.password = filter_password(uri.password)
28
- uri.query = filter_query(uri.query)
60
+ uri.user = filter_user(uri.user, scrub_user, randomize_scrub_length)
61
+ uri.password = filter_password(uri.password, scrub_password,
62
+ randomize_scrub_length)
63
+ uri.query = filter_query(uri.query, regex, randomize_scrub_length, scrub_all,
64
+ whitelist)
29
65
 
30
66
  uri.to_s
31
- rescue
32
- url
33
67
  end
34
68
 
35
- private
36
-
37
69
  # Builds a regex to match with any of the received fields.
38
70
  # The built regex will also match array params like 'user_ids[]'.
39
71
  def build_regex(fields)
@@ -42,23 +74,26 @@ module Rollbar
42
74
  Regexp.new("^#{fields_or}$")
43
75
  end
44
76
 
45
- def filter_user(user)
46
- scrub_user && user ? filtered_value(user) : user
77
+ def filter_user(user, scrub_user, randomize_scrub_length)
78
+ scrub_user && user ? filtered_value(user, randomize_scrub_length) : user
47
79
  end
48
80
 
49
- def filter_password(password)
50
- scrub_password && password ? filtered_value(password) : password
81
+ def filter_password(password, scrub_password, randomize_scrub_length)
82
+ if scrub_password && password
83
+ filtered_value(password,
84
+ randomize_scrub_length)
85
+ else
86
+ password
87
+ end
51
88
  end
52
89
 
53
- def filter_query(query)
90
+ def filter_query(query, regex, randomize_scrub_length, scrub_all, whitelist)
54
91
  return query unless query
55
92
 
56
93
  params = decode_www_form(query)
57
94
 
58
- encoded_query = encode_www_form(filter_query_params(params))
59
-
60
- # We want this to rebuild array params like foo[]=1&foo[]=2
61
- CGI.unescape(encoded_query)
95
+ encode_www_form(filter_query_params(params, regex, randomize_scrub_length,
96
+ scrub_all, whitelist))
62
97
  end
63
98
 
64
99
  def decode_www_form(query)
@@ -66,29 +101,49 @@ module Rollbar
66
101
  end
67
102
 
68
103
  def encode_www_form(params)
69
- URI.encode_www_form(params)
104
+ restore_square_brackets(URI.encode_www_form(params))
105
+ end
106
+
107
+ def restore_square_brackets(query)
108
+ # We want this to rebuild array params like foo[]=1&foo[]=2
109
+ #
110
+ # URI.encode_www_form follows the spec at
111
+ # https://url.spec.whatwg.org/#concept-urlencoded-serializer
112
+ # and percent encodes square brackets. Here we change them back.
113
+ query.gsub('%5B', '[').gsub('%5D', ']')
70
114
  end
71
115
 
72
- def filter_query_params(params)
116
+ def filter_query_params(params, regex, randomize_scrub_length, scrub_all,
117
+ whitelist)
73
118
  params.map do |key, value|
74
- [key, filter_key?(key) ? filtered_value(value) : value]
119
+ [key,
120
+ if filter_key?(key, regex, scrub_all,
121
+ whitelist)
122
+ filtered_value(value, randomize_scrub_length)
123
+ else
124
+ value
125
+ end]
75
126
  end
76
127
  end
77
128
 
78
- def filter_key?(key)
79
- !!(key =~ regex)
129
+ def filter_key?(key, regex, scrub_all, whitelist)
130
+ !(whitelist === key) && (scrub_all || regex === key)
80
131
  end
81
132
 
82
- def filtered_value(value)
133
+ def filtered_value(value, randomize_scrub_length)
83
134
  if randomize_scrub_length
84
135
  random_filtered_value
85
136
  else
86
- '*' * (value.length rescue 8)
137
+ '*' * (begin
138
+ value.length
139
+ rescue StandardError
140
+ 8
141
+ end)
87
142
  end
88
143
  end
89
144
 
90
145
  def random_filtered_value
91
- '*' * (rand(5) + 3)
146
+ '*' * rand(3..7)
92
147
  end
93
148
  end
94
149
  end
@@ -1,4 +1,17 @@
1
1
  module Rollbar
2
2
  module Scrubbers
3
+ module_function
4
+
5
+ def scrub_value(_value)
6
+ if Rollbar.configuration.randomize_scrub_length
7
+ random_filtered_value
8
+ else
9
+ '*' * 6
10
+ end
11
+ end
12
+
13
+ def random_filtered_value
14
+ '*' * rand(3..7)
15
+ end
3
16
  end
4
17
  end
@@ -1,4 +1,5 @@
1
1
  require 'rollbar/truncation/mixin'
2
+ require 'rollbar/util'
2
3
 
3
4
  module Rollbar
4
5
  module Truncation
@@ -10,7 +11,7 @@ module Rollbar
10
11
  end
11
12
 
12
13
  def call(payload)
13
- new_payload = payload.clone
14
+ new_payload = payload
14
15
  body = new_payload['data']['body']
15
16
 
16
17
  if body['trace_chain']
@@ -1,4 +1,5 @@
1
1
  require 'rollbar/truncation/mixin'
2
+ require 'rollbar/util'
2
3
 
3
4
  module Rollbar
4
5
  module Truncation
@@ -10,8 +11,7 @@ module Rollbar
10
11
  end
11
12
 
12
13
  def call(payload)
13
- new_payload = payload.clone
14
- body = new_payload['data']['body']
14
+ body = payload['data']['body']
15
15
 
16
16
  if body['trace_chain']
17
17
  body['trace_chain'] = body['trace_chain'].map do |trace_data|
@@ -21,8 +21,7 @@ module Rollbar
21
21
  body['trace'] = truncate_trace_data(body['trace'])
22
22
  end
23
23
 
24
-
25
- dump(new_payload)
24
+ dump(payload)
26
25
  end
27
26
 
28
27
  def truncate_trace_data(trace_data)
@@ -9,7 +9,7 @@ module Rollbar
9
9
  result.bytesize > MAX_PAYLOAD_SIZE
10
10
  end
11
11
 
12
- def select_frames(frames, range = 150)
12
+ def select_frames(frames, range = 50)
13
13
  return frames unless frames.count > range * 2
14
14
 
15
15
  frames[0, range] + frames[-range, range]
@@ -0,0 +1,126 @@
1
+ require 'rollbar/util'
2
+
3
+ module Rollbar
4
+ module Truncation
5
+ class RemoveAnyKeyStrategy
6
+ include ::Rollbar::Truncation::Mixin
7
+
8
+ attr_accessor :payload, :data, :sizes, :extracted_title
9
+
10
+ def self.call(payload)
11
+ new(payload).call
12
+ end
13
+
14
+ def initialize(payload)
15
+ @payload = payload
16
+ @data = payload['data']
17
+ @extracted_title = extract_title(data['body']) if data['body']
18
+ end
19
+
20
+ def call
21
+ remove_unknown_root_keys
22
+
23
+ json_payload = remove_oversized_data_keys
24
+
25
+ return json_payload if json_payload
26
+
27
+ dump(payload)
28
+ end
29
+
30
+ def remove_unknown_root_keys
31
+ payload.keys.reject { |key| root_keys.include?(key) }.each do |key|
32
+ truncation_key['root'] ||= {}
33
+ size = dump(payload.delete(key)).bytesize
34
+ truncation_key['root'][key] = "unknown root key removed, size: #{size} bytes"
35
+ end
36
+ end
37
+
38
+ def remove_oversized_data_keys
39
+ data_keys.keys.sort { |a, b| data_keys[b] <=> data_keys[a] }.each do |key|
40
+ json_payload = remove_key_and_return_payload(key)
41
+
42
+ return json_payload unless truncate?(json_payload)
43
+ end
44
+
45
+ false
46
+ end
47
+
48
+ def remove_key_and_return_payload(key)
49
+ size = data_keys[key]
50
+
51
+ data.delete(key)
52
+
53
+ replace_message_body if key == 'body'
54
+
55
+ truncation_key[key] = "key removed, size: #{size} bytes"
56
+
57
+ dump(payload)
58
+ end
59
+
60
+ def replace_message_body
61
+ data['body'] = message_key
62
+ data['title'] ||= extracted_title if extracted_title
63
+ end
64
+
65
+ def truncation_key
66
+ @truncation_key ||=
67
+ # initialize the diagnostic key for truncation
68
+ (data['notifier']['diagnostic'] ||= {}) &&
69
+ (data['notifier']['diagnostic']['truncation'] ||= {})
70
+ end
71
+
72
+ def root_keys
73
+ # Valid keys in root of payload
74
+ %w[access_token data]
75
+ end
76
+
77
+ def skip_keys
78
+ # Don't try to truncate these data keys
79
+ %w[notifier uuid title platform language framework level]
80
+ end
81
+
82
+ def message_key
83
+ # use this message if data.body gets removed
84
+ {
85
+ 'message' => {
86
+ 'body' => 'Payload keys removed due to oversized payload. See diagnostic key'
87
+ }
88
+ }
89
+ end
90
+
91
+ def extract_title(body)
92
+ return body['message']['body'] if body['message'] && body['message']['body']
93
+ return extract_title_from_trace(body['trace']) if body['trace']
94
+
95
+ return unless body['trace_chain'] && body['trace_chain'][0]
96
+
97
+ extract_title_from_trace(body['trace_chain'][0])
98
+ end
99
+
100
+ def extract_title_from_trace(trace)
101
+ exception = trace['exception']
102
+
103
+ "#{exception['class']}: #{exception['message']}"
104
+ end
105
+
106
+ def data_keys
107
+ @data_keys ||= {}.tap do |hash|
108
+ data.keys.reject { |key| skip_keys.include?(key) }.each do |key|
109
+ set_key_size(key, hash)
110
+ end
111
+ end
112
+ end
113
+
114
+ def set_key_size(key, hash)
115
+ size = dump(data[key]).bytesize
116
+ hash[key] = size
117
+ rescue ::JSON::GeneratorError
118
+ hash[key] = 0 # don't try to truncate non JSON object
119
+
120
+ # Log it
121
+ truncation_key['non_json_keys'] ||= {}
122
+ truncation_key['non_json_keys'][key] = data[key].class
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,37 @@
1
+ require 'rollbar/util'
2
+
3
+ module Rollbar
4
+ module Truncation
5
+ class RemoveExtraStrategy
6
+ include ::Rollbar::Truncation::Mixin
7
+
8
+ def self.call(payload)
9
+ new.call(payload)
10
+ end
11
+
12
+ def call(payload)
13
+ body = payload['data']['body']
14
+
15
+ delete_message_extra(body)
16
+ delete_trace_chain_extra(body)
17
+ delete_trace_extra(body)
18
+
19
+ dump(payload)
20
+ end
21
+
22
+ def delete_message_extra(body)
23
+ body['message'].delete('extra') if body['message'] && body['message']['extra']
24
+ end
25
+
26
+ def delete_trace_chain_extra(body)
27
+ return unless body['trace_chain'] && body['trace_chain'][0]['extra']
28
+
29
+ body['trace_chain'][0].delete('extra')
30
+ end
31
+
32
+ def delete_trace_extra(body)
33
+ body['trace'].delete('extra') if body['trace'] && body['trace']['extra']
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,21 @@
1
+ require 'rollbar/util'
2
+
3
+ module Rollbar
4
+ module Truncation
5
+ class RemoveRequestStrategy
6
+ include ::Rollbar::Truncation::Mixin
7
+
8
+ def self.call(payload)
9
+ new.call(payload)
10
+ end
11
+
12
+ def call(payload)
13
+ data = payload['data']
14
+
15
+ data.delete('request') if data['request']
16
+
17
+ dump(payload)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -6,7 +6,7 @@ module Rollbar
6
6
  class StringsStrategy
7
7
  include ::Rollbar::Truncation::Mixin
8
8
 
9
- STRING_THRESHOLDS = [1024, 512, 256]
9
+ STRING_THRESHOLDS = [1024, 512, 256, 128].freeze
10
10
 
11
11
  def self.call(payload)
12
12
  new.call(payload)
@@ -14,13 +14,12 @@ module Rollbar
14
14
 
15
15
  def call(payload)
16
16
  result = nil
17
- new_payload = payload.clone
18
17
 
19
18
  STRING_THRESHOLDS.each do |threshold|
20
19
  truncate_proc = truncate_strings_proc(threshold)
21
20
 
22
- ::Rollbar::Util.iterate_and_update(new_payload, truncate_proc)
23
- result = dump(new_payload)
21
+ ::Rollbar::Util.iterate_and_update(payload, truncate_proc)
22
+ result = dump(payload)
24
23
 
25
24
  break unless truncate?(result)
26
25
  end
@@ -30,7 +29,9 @@ module Rollbar
30
29
 
31
30
  def truncate_strings_proc(threshold)
32
31
  proc do |value|
33
- if value.is_a?(String) && value.bytesize > threshold
32
+ # Rollbar::Util.truncate will operate on characters, not bytes,
33
+ # so use value.length, not bytesize.
34
+ if value.is_a?(String) && value.length > threshold
34
35
  Rollbar::Util.truncate(value, threshold)
35
36
  else
36
37
  value
@@ -4,23 +4,29 @@ require 'rollbar/truncation/raw_strategy'
4
4
  require 'rollbar/truncation/frames_strategy'
5
5
  require 'rollbar/truncation/strings_strategy'
6
6
  require 'rollbar/truncation/min_body_strategy'
7
+ require 'rollbar/truncation/remove_request_strategy'
8
+ require 'rollbar/truncation/remove_extra_strategy'
9
+ require 'rollbar/truncation/remove_any_key_strategy'
7
10
 
8
11
  module Rollbar
9
12
  module Truncation
10
13
  extend ::Rollbar::Truncation::Mixin
11
14
 
12
- MAX_PAYLOAD_SIZE = 128 * 1024 # 128kb
15
+ MAX_PAYLOAD_SIZE = 512 * 1024 # 512kb
13
16
  STRATEGIES = [RawStrategy,
14
17
  FramesStrategy,
15
18
  StringsStrategy,
16
- MinBodyStrategy
17
- ]
19
+ MinBodyStrategy,
20
+ RemoveRequestStrategy,
21
+ RemoveExtraStrategy,
22
+ RemoveAnyKeyStrategy].freeze
18
23
 
19
- def self.truncate(payload)
24
+ def self.truncate(payload, attempts = [])
20
25
  result = nil
21
26
 
22
27
  STRATEGIES.each do |strategy|
23
28
  result = strategy.call(payload)
29
+ attempts << result.bytesize
24
30
  break unless truncate?(result)
25
31
  end
26
32
 
@@ -1,24 +1,55 @@
1
1
  module Rollbar
2
2
  module Util
3
- module Hash
4
- def self.deep_stringify_keys(hash)
3
+ module Hash # :nodoc:
4
+ def self.deep_stringify_keys(hash, seen = {})
5
+ seen.compare_by_identity
6
+ return if seen[hash]
7
+
8
+ seen[hash] = true
9
+ replace_seen_children(hash, seen)
10
+
5
11
  hash.reduce({}) do |h, (key, value)|
6
- h[key.to_s] = map_value(value, :deep_stringify_keys)
12
+ h[key.to_s] = map_value(value, :deep_stringify_keys, seen)
7
13
 
8
14
  h
9
15
  end
10
16
  end
11
17
 
12
- def self.map_value(thing, m)
18
+ def self.map_value(thing, meth, seen)
13
19
  case thing
14
20
  when ::Hash
15
- send(m, thing)
21
+ send(meth, thing, seen)
16
22
  when Array
17
- thing.map { |v| map_value(v, m) }
23
+ if seen[thing]
24
+ thing
25
+ else
26
+ seen[thing] = true
27
+ replace_seen_children(thing, seen)
28
+ thing.map { |v| map_value(v, meth, seen) }
29
+ end
18
30
  else
19
31
  thing
20
32
  end
21
33
  end
34
+
35
+ def self.replace_seen_children(thing, seen)
36
+ case thing
37
+ when ::Hash
38
+ thing.keys.each do |key| # rubocop:disable Style/HashEachMethods
39
+ if seen[thing[key]]
40
+ thing[key] =
41
+ "removed circular reference: #{thing[key]}"
42
+ end
43
+ end
44
+ when Array
45
+ thing.each_with_index do |_, i|
46
+ if seen[thing[i]]
47
+ thing[i] =
48
+ "removed circular reference: #{thing[i]}"
49
+ end
50
+ end
51
+ end
52
+ end
22
53
  end
23
54
  end
24
55
  end
@@ -0,0 +1,33 @@
1
+ module Rollbar
2
+ module Util
3
+ module IPAnonymizer
4
+ require 'ipaddr'
5
+
6
+ def self.anonymize_ip(ip_string)
7
+ return ip_string unless Rollbar.configuration.anonymize_user_ip
8
+
9
+ ip = IPAddr.new(ip_string)
10
+ return anonymize_ipv6 ip if ip.ipv6?
11
+ return anonymize_ipv4 ip if ip.ipv4?
12
+ rescue StandardError
13
+ nil
14
+ end
15
+
16
+ def self.anonymize_ipv4(ip)
17
+ ip_parts = ip.to_s.split '.'
18
+
19
+ ip_parts[ip_parts.count - 1] = '0'
20
+
21
+ IPAddr.new(ip_parts.join('.')).to_s
22
+ end
23
+
24
+ def self.anonymize_ipv6(ip)
25
+ ip_parts = ip.to_s.split ':'
26
+
27
+ ip_string = "#{ip_parts[0..2].join(':')}:0000:0000:0000:0000:0000"
28
+
29
+ IPAddr.new(ip_string).to_s
30
+ end
31
+ end
32
+ end
33
+ end
@@ -13,7 +13,7 @@ module Rollbar
13
13
  obfuscated_ip_int32 = ip_int32 ^ secret_int32 % (2 << 31)
14
14
 
15
15
  IPAddr.new(obfuscated_ip_int32, Socket::AF_INET).to_s
16
- rescue
16
+ rescue StandardError
17
17
  nil
18
18
  end
19
19
  end