sqreen 1.18.6-java → 1.20.0-java

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 (190) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +27 -0
  3. data/lib/sqreen/actions.rb +2 -0
  4. data/lib/sqreen/actions/actions_index.rb +16 -0
  5. data/lib/sqreen/actions/base.rb +4 -10
  6. data/lib/sqreen/actions/block_ip.rb +2 -0
  7. data/lib/sqreen/actions/block_user.rb +2 -0
  8. data/lib/sqreen/actions/ip_range_indexed_action_class.rb +4 -24
  9. data/lib/sqreen/actions/ip_ranges_index.rb +32 -11
  10. data/lib/sqreen/actions/redirect_ip.rb +2 -0
  11. data/lib/sqreen/actions/redirect_user.rb +2 -0
  12. data/lib/sqreen/actions/repository.rb +27 -8
  13. data/lib/sqreen/actions/unknown_action_type.rb +4 -0
  14. data/lib/sqreen/actions/user_action_class.rb +5 -30
  15. data/lib/sqreen/actions/users_index.rb +35 -0
  16. data/lib/sqreen/agent.rb +2 -1
  17. data/lib/sqreen/aggregated_metric.rb +25 -0
  18. data/lib/sqreen/attack_blocked.rb +2 -0
  19. data/lib/sqreen/binding_accessor.rb +2 -0
  20. data/lib/sqreen/binding_accessor/path_elem.rb +2 -0
  21. data/lib/sqreen/binding_accessor/transforms.rb +8 -1
  22. data/lib/sqreen/call_countable.rb +2 -0
  23. data/lib/sqreen/capped_queue.rb +2 -0
  24. data/lib/sqreen/cb.rb +2 -0
  25. data/lib/sqreen/cb_tree.rb +2 -0
  26. data/lib/sqreen/condition_evaluator.rb +2 -0
  27. data/lib/sqreen/conditionable.rb +2 -0
  28. data/lib/sqreen/configuration.rb +19 -1
  29. data/lib/sqreen/context.rb +2 -0
  30. data/lib/sqreen/default_cb.rb +2 -0
  31. data/lib/sqreen/deferred_logger.rb +2 -0
  32. data/lib/sqreen/deliveries.rb +2 -0
  33. data/lib/sqreen/deliveries/batch.rb +6 -1
  34. data/lib/sqreen/deliveries/simple.rb +6 -0
  35. data/lib/sqreen/dependency.rb +3 -1
  36. data/lib/sqreen/dependency/detector.rb +22 -14
  37. data/lib/sqreen/dependency/libsqreen.rb +4 -0
  38. data/lib/sqreen/dependency/new_relic.rb +2 -0
  39. data/lib/sqreen/dependency/rack.rb +10 -5
  40. data/lib/sqreen/dependency/rails.rb +4 -0
  41. data/lib/sqreen/dependency/sentry.rb +2 -0
  42. data/lib/sqreen/dependency/sinatra.rb +12 -1
  43. data/lib/sqreen/encoding_sanitizer.rb +2 -0
  44. data/lib/sqreen/error_handling_middleware.rb +2 -0
  45. data/lib/sqreen/event.rb +9 -5
  46. data/lib/sqreen/events/attack.rb +25 -18
  47. data/lib/sqreen/events/remote_exception.rb +2 -22
  48. data/lib/sqreen/events/request_record.rb +17 -70
  49. data/lib/sqreen/exception.rb +2 -0
  50. data/lib/sqreen/formatter_with_tid.rb +2 -0
  51. data/lib/sqreen/framework_cb.rb +2 -0
  52. data/lib/sqreen/frameworks.rb +2 -0
  53. data/lib/sqreen/frameworks/generic.rb +2 -0
  54. data/lib/sqreen/frameworks/rails.rb +1 -0
  55. data/lib/sqreen/frameworks/rails3.rb +2 -0
  56. data/lib/sqreen/frameworks/request_recorder.rb +15 -2
  57. data/lib/sqreen/frameworks/sinatra.rb +2 -0
  58. data/lib/sqreen/frameworks/sqreen_test.rb +2 -0
  59. data/lib/sqreen/graft.rb +12 -0
  60. data/lib/sqreen/graft/call.rb +150 -0
  61. data/lib/sqreen/{dependency → graft}/callback.rb +12 -4
  62. data/lib/sqreen/graft/hook.rb +316 -0
  63. data/lib/sqreen/{dependency → graft}/hook_point.rb +152 -33
  64. data/lib/sqreen/graft/hook_point_error.rb +10 -0
  65. data/lib/sqreen/invalid_signature_exception.rb +2 -0
  66. data/lib/sqreen/js.rb +2 -0
  67. data/lib/sqreen/js/call_context.rb +2 -0
  68. data/lib/sqreen/js/context_pool.rb +2 -0
  69. data/lib/sqreen/js/exec_js_runnable.rb +2 -0
  70. data/lib/sqreen/js/execjs_adapter.rb +2 -0
  71. data/lib/sqreen/js/executable_js.rb +2 -0
  72. data/lib/sqreen/js/js_service.rb +2 -0
  73. data/lib/sqreen/js/js_service_adapter.rb +2 -0
  74. data/lib/sqreen/js/mini_racer_adapter.rb +2 -0
  75. data/lib/sqreen/js/mini_racer_executable_js.rb +2 -0
  76. data/lib/sqreen/js/thread_local_exec_js_runnable.rb +2 -0
  77. data/lib/sqreen/kit/signals/specialized/aggregated_metric.rb +72 -0
  78. data/lib/sqreen/kit/signals/specialized/attack.rb +57 -0
  79. data/lib/sqreen/kit/signals/specialized/binning_metric.rb +76 -0
  80. data/lib/sqreen/kit/signals/specialized/http_trace.rb +26 -0
  81. data/lib/sqreen/kit/signals/specialized/sdk_track_call.rb +50 -0
  82. data/lib/sqreen/kit/signals/specialized/sqreen_exception.rb +57 -0
  83. data/lib/sqreen/{backport.rb → legacy.rb} +3 -2
  84. data/lib/sqreen/{instrumentation.rb → legacy/instrumentation.rb} +31 -2
  85. data/lib/sqreen/legacy/old_event_submission_strategy.rb +221 -0
  86. data/lib/sqreen/legacy/waf_redactions.rb +49 -0
  87. data/lib/sqreen/log.rb +2 -0
  88. data/lib/sqreen/log/loggable.rb +28 -0
  89. data/lib/sqreen/logger.rb +2 -0
  90. data/lib/sqreen/metrics.rb +2 -0
  91. data/lib/sqreen/metrics/average.rb +2 -0
  92. data/lib/sqreen/metrics/base.rb +5 -0
  93. data/lib/sqreen/metrics/binning.rb +2 -0
  94. data/lib/sqreen/metrics/collect.rb +2 -0
  95. data/lib/sqreen/metrics/sum.rb +2 -0
  96. data/lib/sqreen/metrics_store.rb +24 -12
  97. data/lib/sqreen/metrics_store/already_registered_metric.rb +2 -0
  98. data/lib/sqreen/metrics_store/unknown_metric.rb +2 -0
  99. data/lib/sqreen/metrics_store/unregistered_metric.rb +2 -0
  100. data/lib/sqreen/middleware.rb +2 -0
  101. data/lib/sqreen/mono_time.rb +2 -0
  102. data/lib/sqreen/node.rb +2 -0
  103. data/lib/sqreen/not_implemented_yet.rb +2 -0
  104. data/lib/sqreen/null_logger.rb +2 -0
  105. data/lib/sqreen/payload_creator.rb +2 -0
  106. data/lib/sqreen/payload_creator/header_section.rb +2 -0
  107. data/lib/sqreen/performance_notifications.rb +2 -0
  108. data/lib/sqreen/performance_notifications/binned_metrics.rb +10 -2
  109. data/lib/sqreen/performance_notifications/log.rb +2 -0
  110. data/lib/sqreen/performance_notifications/log_performance.rb +2 -0
  111. data/lib/sqreen/performance_notifications/metrics.rb +2 -0
  112. data/lib/sqreen/performance_notifications/newrelic.rb +2 -0
  113. data/lib/sqreen/prefix.rb +2 -0
  114. data/lib/sqreen/rails_middleware.rb +2 -0
  115. data/lib/sqreen/remote_command.rb +2 -0
  116. data/lib/sqreen/remote_command/failure_output.rb +5 -0
  117. data/lib/sqreen/rules.rb +6 -2
  118. data/lib/sqreen/rules/attrs.rb +2 -0
  119. data/lib/sqreen/rules/auth_track_cb.rb +2 -0
  120. data/lib/sqreen/rules/binding_accessor_matcher_cb.rb +2 -0
  121. data/lib/sqreen/rules/binding_accessor_metrics.rb +2 -0
  122. data/lib/sqreen/rules/blacklist_ips_cb.rb +2 -0
  123. data/lib/sqreen/rules/count_http_codes.rb +2 -0
  124. data/lib/sqreen/rules/crawler_user_agent_matches_cb.rb +2 -0
  125. data/lib/sqreen/rules/crawler_user_agent_matches_metrics_cb.rb +2 -0
  126. data/lib/sqreen/rules/custom_error_cb.rb +2 -0
  127. data/lib/sqreen/rules/devise_auth_track_cb.rb +2 -0
  128. data/lib/sqreen/rules/devise_signup_track_cb.rb +2 -0
  129. data/lib/sqreen/rules/execjs_cb.rb +2 -0
  130. data/lib/sqreen/rules/headers_insert_cb.rb +7 -0
  131. data/lib/sqreen/rules/matcher_rule.rb +2 -0
  132. data/lib/sqreen/rules/not_found_cb.rb +7 -0
  133. data/lib/sqreen/rules/rails_parameters_cb.rb +2 -0
  134. data/lib/sqreen/rules/record_request_context.rb +2 -0
  135. data/lib/sqreen/rules/regexp_rule_cb.rb +2 -0
  136. data/lib/sqreen/rules/rule_cb.rb +4 -0
  137. data/lib/sqreen/rules/run_req_start_actions.rb +3 -1
  138. data/lib/sqreen/rules/run_user_actions.rb +3 -1
  139. data/lib/sqreen/rules/shell_env_cb.rb +2 -0
  140. data/lib/sqreen/rules/signup_track_cb.rb +2 -0
  141. data/lib/sqreen/rules/update_request_context.rb +2 -0
  142. data/lib/sqreen/rules/url_matches_cb.rb +2 -0
  143. data/lib/sqreen/rules/user_agent_matches_cb.rb +2 -0
  144. data/lib/sqreen/rules/waf_cb.rb +41 -16
  145. data/lib/sqreen/rules/xss_cb.rb +2 -0
  146. data/lib/sqreen/run_when_called_cb.rb +2 -0
  147. data/lib/sqreen/runner.rb +68 -12
  148. data/lib/sqreen/runtime_infos.rb +2 -0
  149. data/lib/sqreen/safe_json.rb +2 -0
  150. data/lib/sqreen/sdk.rb +4 -0
  151. data/lib/sqreen/sensitive_data_redactor.rb +21 -31
  152. data/lib/sqreen/serializer.rb +2 -0
  153. data/lib/sqreen/session.rb +41 -37
  154. data/lib/sqreen/shared_storage.rb +2 -0
  155. data/lib/sqreen/shared_storage23.rb +2 -0
  156. data/lib/sqreen/shrink_wrap.rb +16 -0
  157. data/lib/sqreen/signals/conversions.rb +283 -0
  158. data/lib/sqreen/signals/http_trace_redaction.rb +111 -0
  159. data/lib/sqreen/signals/signals_submission_strategy.rb +78 -0
  160. data/lib/sqreen/signature_verifier.rb +2 -0
  161. data/lib/sqreen/sinatra_middleware.rb +2 -0
  162. data/lib/sqreen/sqreen_signed_verifier.rb +2 -0
  163. data/lib/sqreen/token_invalid_exception.rb +2 -0
  164. data/lib/sqreen/token_not_found_exception.rb +2 -0
  165. data/lib/sqreen/trie.rb +2 -0
  166. data/lib/sqreen/unauthorized.rb +2 -0
  167. data/lib/sqreen/util.rb +5 -0
  168. data/lib/sqreen/util/capped_array.rb +2 -0
  169. data/lib/sqreen/util/capped_hash.rb +2 -0
  170. data/lib/sqreen/util/capped_string.rb +2 -0
  171. data/lib/sqreen/util/capper.rb +2 -0
  172. data/lib/sqreen/version.rb +3 -1
  173. data/lib/sqreen/waf_error.rb +2 -0
  174. data/lib/sqreen/weave.rb +12 -0
  175. data/lib/sqreen/weave/hardcoded.rb +19 -0
  176. data/lib/sqreen/weave/instrumentor.rb +48 -0
  177. data/lib/sqreen/weave/legacy.rb +12 -0
  178. data/lib/sqreen/weave/legacy/instrumentation.rb +406 -0
  179. data/lib/sqreen/web_server.rb +2 -0
  180. data/lib/sqreen/web_server/generic.rb +2 -0
  181. data/lib/sqreen/web_server/passenger.rb +2 -0
  182. data/lib/sqreen/web_server/puma.rb +2 -0
  183. data/lib/sqreen/web_server/rainbows.rb +2 -0
  184. data/lib/sqreen/web_server/thin.rb +2 -0
  185. data/lib/sqreen/web_server/unicorn.rb +2 -0
  186. data/lib/sqreen/web_server/webrick.rb +2 -0
  187. data/lib/sqreen/worker.rb +2 -0
  188. metadata +65 -9
  189. data/lib/sqreen/backport/original_name.rb +0 -86
  190. data/lib/sqreen/dependency/hook.rb +0 -102
@@ -1,3 +1,5 @@
1
+ # typed: true
2
+
1
3
  # Copyright (c) 2015 Sqreen. All Rights Reserved.
2
4
  # Please refer to our terms for more information: https://www.sqreen.com/terms.html
3
5
 
@@ -59,7 +61,7 @@ module Sqreen
59
61
  obj.each do |k, v|
60
62
  ck = k.is_a?(String) ? k.downcase : k
61
63
  if @keys.include?(ck)
62
- redacted << v
64
+ redacted += SensitiveDataRedactor.all_strings(v)
63
65
  v = MASK
64
66
  else
65
67
  v, r = redact(v)
@@ -72,39 +74,27 @@ module Sqreen
72
74
  [result, redacted]
73
75
  end
74
76
 
75
- def redact_attacks!(attacks, values)
76
- return attacks if values.empty?
77
-
78
- values = values.map { |v| v.downcase if v.is_a?(String) }
79
-
80
- attacks.each do |e|
81
- next(e) unless e[:infos]
82
- next(e) unless e[:infos][:waf_data]
83
-
84
- parsed = JSON.parse(e[:infos][:waf_data])
85
- redacted = parsed.each do |w|
86
- next unless (filters = w['filter'])
87
-
88
- filters.each do |f|
89
- next unless (v = f['resolved_value'])
90
- next unless values.include?(v.downcase)
77
+ class << self
78
+ def all_strings(v)
79
+ accum = []
80
+ all_strings_impl(v, accum)
81
+ accum
82
+ end
91
83
 
92
- f['match_status'] = MASK
93
- f['resolved_value'] = MASK
84
+ private
85
+
86
+ def all_strings_impl(obj, accum)
87
+ case obj
88
+ when String
89
+ accum << obj
90
+ when Array
91
+ obj.each { |el| all_strings_impl(el, accum) }
92
+ when Hash
93
+ obj.each do |k, v|
94
+ all_strings_impl(k, accum)
95
+ all_strings_impl(v, accum)
94
96
  end
95
97
  end
96
- e[:infos][:waf_data] = JSON.dump(redacted)
97
- end
98
- end
99
-
100
- def redact_exceptions!(exceptions, values)
101
- return exceptions if values.empty?
102
-
103
- exceptions.each do |e|
104
- next(e) unless e[:infos]
105
- next(e) unless e[:infos][:waf]
106
-
107
- e[:infos][:waf].delete(:args)
108
98
  end
109
99
  end
110
100
  end
@@ -1,3 +1,5 @@
1
+ # typed: true
2
+
1
3
  # Copyright (c) 2015 Sqreen. All Rights Reserved.
2
4
  # Please refer to our terms for more information: https://www.sqreen.com/terms.html
3
5
 
@@ -1,3 +1,5 @@
1
+ # typed: ignore
2
+
1
3
  # Copyright (c) 2015 Sqreen. All Rights Reserved.
2
4
  # Please refer to our terms for more information: https://www.sqreen.com/terms.html
3
5
 
@@ -9,6 +11,10 @@ require 'sqreen/events/attack'
9
11
  require 'sqreen/events/request_record'
10
12
  require 'sqreen/exception'
11
13
  require 'sqreen/safe_json'
14
+ require 'sqreen/kit'
15
+ require 'sqreen/kit/configuration'
16
+ require 'sqreen/signals/signals_submission_strategy'
17
+ require 'sqreen/legacy/old_event_submission_strategy'
12
18
 
13
19
  require 'net/https'
14
20
  require 'uri'
@@ -39,13 +45,12 @@ module Sqreen
39
45
  RETRY_MANY = 301
40
46
 
41
47
  MUTEX = Mutex.new
42
- METRICS_KEY = 'metrics'.freeze
43
48
 
44
49
  @@path_prefix = '/sqreen/v0/'
45
50
 
46
51
  attr_accessor :request_compression
47
52
 
48
- def initialize(server_url, token, app_name = nil)
53
+ def initialize(server_url, token, app_name = nil, proxy_url = nil)
49
54
  @token = token
50
55
  @app_name = app_name
51
56
  @session_id = nil
@@ -57,16 +62,35 @@ module Sqreen
57
62
  uri = parse_uri(server_url)
58
63
  use_ssl = (uri.scheme == 'https')
59
64
 
65
+ proxy_params = []
66
+ if proxy_url
67
+ proxy_uri = parse_uri(proxy_url)
68
+ proxy_params = [proxy_uri.host, proxy_uri.port, proxy_uri.user, proxy_uri.password]
69
+ end
70
+
60
71
  @req_nb = 0
61
72
 
62
- @http = Net::HTTP.new(uri.host, uri.port)
73
+ @http = Net::HTTP.new(uri.host, uri.port, *proxy_params)
63
74
  @http.use_ssl = use_ssl
75
+ @http.verify_mode = OpenSSL::SSL::VERIFY_NONE if ENV['SQREEN_SSL_NO_VERIFY'] # for testing
64
76
  if use_ssl
65
77
  cert_file = File.join(File.dirname(__FILE__), 'ca.crt')
66
78
  cert_store = OpenSSL::X509::Store.new
67
79
  cert_store.add_file cert_file
68
80
  @http.cert_store = cert_store
69
81
  end
82
+ self.use_signals = false
83
+ end
84
+
85
+ def use_signals=(do_use)
86
+ return if do_use == @use_signals
87
+
88
+ @use_signals = do_use
89
+ if do_use
90
+ @evt_sub_strategy = Sqreen::Signals::SignalsSubmissionStrategy.new
91
+ else
92
+ @evt_sub_strategy = Sqreen::Legacy::OldEventSubmissionStrategy.new(method(:post))
93
+ end
70
94
  end
71
95
 
72
96
  def parse_uri(uri)
@@ -233,6 +257,8 @@ module Sqreen
233
257
  end
234
258
  Sqreen.log.info 'Login success.'
235
259
  @session_id = res['session_id']
260
+ Kit::Configuration.session_key = @session_id
261
+ Kit.reset
236
262
  Sqreen.log.debug { "received session_id #{@session_id}" }
237
263
  Sqreen.logged_in = true
238
264
  res
@@ -244,20 +270,24 @@ module Sqreen
244
270
 
245
271
  def heartbeat(cmd_res = {}, metrics = [])
246
272
  payload = {}
247
- payload['metrics'] = metrics unless metrics.nil? || metrics.empty?
273
+ unless metrics.nil? || metrics.empty?
274
+ # never reached with signals
275
+ payload['metrics'] = metrics.map do |m|
276
+ Sqreen::Legacy::EventToHash.convert_agg_metric(m)
277
+ end
278
+ end
248
279
  payload['command_results'] = cmd_res unless cmd_res.nil? || cmd_res.empty?
249
280
 
250
281
  post('app-beat', payload.empty? ? nil : payload, {}, RETRY_MANY)
251
282
  end
252
283
 
253
284
  def post_metrics(metrics)
254
- return if metrics.nil? || metrics.empty?
255
- payload = { METRICS_KEY => metrics }
256
- post(METRICS_KEY, payload, {}, RETRY_MANY)
285
+ @evt_sub_strategy.post_metrics(metrics)
257
286
  end
258
287
 
288
+ # XXX never called
259
289
  def post_attack(attack)
260
- post('attack', attack.to_hash, {}, RETRY_MANY)
290
+ @evt_sub_strategy.post_attack(attack)
261
291
  end
262
292
 
263
293
  def post_bundle(bundle_sig, dependencies)
@@ -269,33 +299,17 @@ module Sqreen
269
299
  end
270
300
 
271
301
  def post_request_record(request_record)
272
- post('request_record', request_record.to_hash, {}, RETRY_MANY)
302
+ @evt_sub_strategy.post_request_record(request_record)
273
303
  end
274
304
 
275
305
  # Post an exception to Sqreen for analysis
276
306
  # @param exception [RemoteException] Exception and context to be sent over
277
307
  def post_sqreen_exception(exception)
278
- post('sqreen_exception', exception.to_hash, {}, 5)
279
- rescue StandardError => e
280
- Sqreen.log.warn(format('Could not post exception (network down? %s) %s',
281
- e.inspect,
282
- exception.to_hash.inspect))
283
- nil
308
+ @evt_sub_strategy.post_sqreen_exception(exception)
284
309
  end
285
310
 
286
- BATCH_KEY = 'batch'.freeze
287
- EVENT_TYPE_KEY = 'event_type'.freeze
288
311
  def post_batch(events)
289
- batch = events.map do |event|
290
- h = event.to_hash
291
- h[EVENT_TYPE_KEY] = event_kind(event)
292
- h
293
- end
294
- Sqreen.log.debug do
295
- tally = Hash[events.group_by(&:class).map{ |k,v| [k, v.count] }]
296
- "Doing batch with the following tally of event types: #{tally}"
297
- end
298
- post(BATCH_KEY, { BATCH_KEY => batch }, {}, RETRY_MANY)
312
+ @evt_sub_strategy.post_batch(events)
299
313
  end
300
314
 
301
315
  # Perform agent logout
@@ -311,15 +325,5 @@ module Sqreen
311
325
  Sqreen.logged_in = false
312
326
  disconnect
313
327
  end
314
-
315
- protected
316
-
317
- def event_kind(event)
318
- case event
319
- when Sqreen::RemoteException then 'sqreen_exception'
320
- when Sqreen::Attack then 'attack'
321
- when Sqreen::RequestRecord then 'request_record'
322
- end
323
- end
324
328
  end
325
329
  end
@@ -1,3 +1,5 @@
1
+ # typed: false
2
+
1
3
  # Copyright (c) 2015 Sqreen. All Rights Reserved.
2
4
  # Please refer to our terms for more information: https://www.sqreen.com/terms.html
3
5
 
@@ -1,3 +1,5 @@
1
+ # typed: ignore
2
+
1
3
  # Copyright (c) 2015 Sqreen. All Rights Reserved.
2
4
  # Please refer to our terms for more information: https://www.sqreen.com/terms.html
3
5
 
@@ -0,0 +1,16 @@
1
+ # typed: true
2
+
3
+ # Copyright (c) 2015 Sqreen. All Rights Reserved.
4
+ # Please refer to our terms for more information: https://www.sqreen.com/terms.html
5
+
6
+ module Sqreen
7
+ class ShrinkWrap
8
+ def initialize(app)
9
+ @app = app
10
+ end
11
+
12
+ def call(env)
13
+ @app.call(env)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,283 @@
1
+ require 'sqreen/version'
2
+ require 'sqreen/rules/rule_cb'
3
+ require 'sqreen/metrics/base'
4
+ require 'sqreen/metrics/binning'
5
+ require 'sqreen/signals/http_trace_redaction'
6
+ require 'sqreen/kit/signals/signal_attributes'
7
+ require 'sqreen/kit/signals/specialized/aggregated_metric'
8
+ require 'sqreen/kit/signals/specialized/attack'
9
+ require 'sqreen/kit/signals/specialized/binning_metric'
10
+ require 'sqreen/kit/signals/specialized/sqreen_exception'
11
+ require 'sqreen/kit/signals/specialized/http_trace'
12
+ require 'sqreen/kit/signals/specialized/sdk_track_call'
13
+
14
+ module Sqreen
15
+ module Signals
16
+ module Conversions # rubocop:disable Metrics/ModuleLength
17
+ class << self
18
+ # @param [Sqreen::AggregatedMetric] agg
19
+ # @return [Sqreen::Kit::Signals::Metric]
20
+ def convert_metric_sample(agg)
21
+ attrs = {
22
+ signal_name: "sq.agent.metric.#{agg.name}",
23
+ source: if agg.rule
24
+ "sqreen:rules:#{agg.rule.rulespack_id}:#{agg.rule.rule_name}"
25
+ else
26
+ agent_gen_source
27
+ end,
28
+ time: agg.finish,
29
+ }
30
+
31
+ if agg.metric.is_a?(Sqreen::Metric::Binning)
32
+ conv_binning_metric(agg, attrs)
33
+ else
34
+ conv_generic_metric(agg, attrs)
35
+ end
36
+ end
37
+
38
+ # @param [Sqreen::Attack] attack
39
+ # XXX: not used because we don't use Sqreen::Attack
40
+ def convert_attack(attack)
41
+ # no need to set actor/context as we only include them in request records/traces
42
+ Kit::Signals::Specialized::Attack.new(
43
+ signal_name: "sq.agent.attack.#{attack.attack_type}",
44
+ source: "sqreen:rule:#{attack.rulespack_id}:#{attack.rule_name}",
45
+ time: attack.time,
46
+ location: Kit::Signals::Location.new(stack_trace: attack.backtrace),
47
+ payload: Kit::Signals::Specialized::Attack::Payload.new(
48
+ test: attack.test?,
49
+ block: attack.block?,
50
+ infos: attack.infos
51
+ )
52
+ )
53
+ end
54
+
55
+ # see Sqreen::Rules::RuleCB.record_event
56
+ def convert_unstructured_attack(payload)
57
+ Kit::Signals::Specialized::Attack.new(
58
+ signal_name: "sq.agent.attack.#{payload[:attack_type]}",
59
+ source: "sqreen:rule:#{payload[:rulespack_id]}:#{payload[:rule_name]}",
60
+ time: payload[:time],
61
+ location: (Kit::Signals::Location.new(stack_trace: payload[:backtrace]) if payload[:backtrace]),
62
+ payload: Kit::Signals::Specialized::Attack::Payload.new(
63
+ test: payload[:test],
64
+ block: payload[:block],
65
+ infos: payload[:infos]
66
+ )
67
+ )
68
+ end
69
+
70
+ # @param [Sqreen::RemoteException] exception
71
+ # @return [Sqreen::Kit::Signals::Specialized::SqreenException]
72
+ def convert_exception(exception)
73
+ payload = exception.payload
74
+
75
+ infos = payload['client_ip'] ? { client_ip: payload['client_ip'] } : {}
76
+ infos.merge!(payload['infos'] || {})
77
+
78
+ Kit::Signals::Specialized::SqreenException.new(
79
+ source: if payload['rule_name']
80
+ "sqreen:rule:#{payload['rulespack_id']}:#{payload['rule_name']}"
81
+ else
82
+ agent_gen_source
83
+ end,
84
+ time: exception.time,
85
+ ruby_exception: payload['exception'],
86
+ infos: infos
87
+ )
88
+ end
89
+
90
+ # see Sqreen::Rules::RuleCB.record_exception
91
+ # @param [Hash] payload
92
+ # @return [Sqreen::Kit::Signals::Specialized::SqreenException]
93
+ def convert_unstructured_exception(payload)
94
+ Kit::Signals::Specialized::SqreenException.new(
95
+ source: "sqreen:rule:#{payload[:rulespack_id]}:#{payload[:rule_name]}",
96
+ time: payload[:time],
97
+ ruby_exception: payload[:exception],
98
+ infos: payload[:infos]
99
+ )
100
+ end
101
+
102
+ # @param [Sqreen::RequestRecord] req_rec
103
+ # @return [Sqreen::Kit::Signals::Specialized::HttpTrace]
104
+ def convert_req_record(req_rec)
105
+ payload = req_rec.payload
106
+
107
+ request_p = payload['request']
108
+ id_args = req_rec.last_identify_args
109
+ identifiers = id_args[0] if id_args
110
+ traits = id_args[1] if id_args
111
+
112
+ observed = payload[:observed] || {}
113
+ signals = []
114
+ signals += (observed[:attacks] || [])
115
+ .map { |att| convert_unstructured_attack(att) }
116
+ signals += (observed[:sqreen_exceptions] || [])
117
+ .map { |sq_exc| convert_unstructured_exception(sq_exc) }
118
+ signals += req_rec.processed_sdk_calls
119
+ .select { |h| h[:name] == :track }
120
+ .map { |h| convert_track(h) }
121
+
122
+ trace = Kit::Signals::Specialized::HttpTrace.new(
123
+ actor: Kit::Signals::Actor.new(
124
+ ip_addresses: [request_p[:client_ip]].compact,
125
+ user_agent: request_p[:user_agent],
126
+ identifiers: identifiers,
127
+ traits: traits,
128
+ ),
129
+ location_infra: location_infra,
130
+ context: convert_request(request_p,
131
+ payload['response'],
132
+ payload['headers'],
133
+ payload['params']),
134
+ data: signals
135
+ )
136
+ HttpTraceRedaction.redact_trace!(trace, req_rec.redactor)
137
+ trace
138
+ end
139
+
140
+ # @param [Array<Sqreen::Kit::Signals::Signal|Sqreen::Kit::Signals::Trace>] batch
141
+ def convert_batch(batch)
142
+ batch.map do |evt|
143
+ case evt
144
+ when RemoteException
145
+ convert_exception(evt)
146
+ when AggregatedMetric
147
+ convert_metric_sample(evt)
148
+ when RequestRecord
149
+ convert_req_record(evt)
150
+ else
151
+ raise NotImplementedError, "Unknown type of event in batch: #{evt}"
152
+ end
153
+ end
154
+ end
155
+
156
+ private
157
+
158
+ def agent_gen_source
159
+ "sqreen:agent:ruby:#{Sqreen::VERSION}"
160
+ end
161
+
162
+ def location_infra
163
+ @location_infra ||= begin
164
+ Kit::Signals::LocationInfra.new(
165
+ agent_version: Sqreen::VERSION,
166
+ os_type: RuntimeInfos.os[:os_type],
167
+ hostname: RuntimeInfos.hostname,
168
+ runtime_type: RuntimeInfos.runtime[:runtime_type],
169
+ runtime_version: RuntimeInfos.runtime[:runtime_version],
170
+ libsqreen_version: RuntimeInfos.libsqreen_version,
171
+ )
172
+ end
173
+ end
174
+
175
+ # see Sqreen::RequestRecord.processed_sdk_calls
176
+ def convert_track(call_info)
177
+ options = call_info[:args][1] || {}
178
+ Kit::Signals::Specialized::SdkTrackCall.new(
179
+ signal_name: "sq.sdk.#{call_info[:args][0]}",
180
+ time: call_info[:time],
181
+ payload: Kit::Signals::Specialized::SdkTrackCall::Payload.new(
182
+ properties: options[:properties],
183
+ user_identifiers: options[:user_identifiers]
184
+ )
185
+ )
186
+ end
187
+
188
+ # @param [Hash] req_payload
189
+ # @param [Hash] headers_payload
190
+ # @param [Hash] params_payload
191
+ # see the PayloadCreator abomination for reference
192
+ # TODO: do not convert from the old payload to the new payload
193
+ # Have an intermediate object that gets the data from the framework.
194
+ # (Or convert directly from the framework, but this needs to be
195
+ # done during the request, not just before event is transmitted)
196
+ def convert_request(req_payload, resp_payload, headers_payload, params_payload)
197
+ req_payload ||= {}
198
+ headers_payload ||= {}
199
+ resp_payload ||= {}
200
+ params_payload ||= {}
201
+
202
+ other = params_payload['other']
203
+ other = merge_hash_append(other, params_payload['rack'])
204
+ other = merge_hash_append(other, params_payload['grape_params'])
205
+ other = merge_hash_append(other, params_payload['rack_routing'])
206
+
207
+ Sqreen::Kit::Signals::Context::HttpContext.new(
208
+ {
209
+ rid: req_payload[:rid],
210
+ headers: headers_payload,
211
+ user_agent: req_payload[:user_agent],
212
+ scheme: req_payload[:scheme],
213
+ verb: req_payload[:verb],
214
+ host: req_payload[:host],
215
+ port: req_payload[:port],
216
+ remote_ip: req_payload[:remote_ip],
217
+ remote_port: req_payload[:remote_port] || 0,
218
+ path: req_payload[:path],
219
+ referer: req_payload[:referer],
220
+ params_query: params_payload['query'],
221
+ params_form: params_payload['form'],
222
+ params_other: other,
223
+ # endpoint, is_reveal_replayed not set
224
+ status: resp_payload[:status],
225
+ content_length: resp_payload[:content_length],
226
+ content_type: resp_payload[:content_type],
227
+ }
228
+ )
229
+ end
230
+
231
+ def merge_hash_append(hash1, hash2)
232
+ return nil if hash1.nil? && hash2.nil?
233
+ return hash1 if hash2.nil? || hash2.empty?
234
+ return hash2 if hash1.nil? || hash1.empty?
235
+
236
+ pairs = (hash1.keys + hash2.keys).map do |key|
237
+ values1 = hash1[key]
238
+ values2 = hash2[key]
239
+ values = [values1, values2].compact
240
+ values = values.first if values.size == 1
241
+ [key, values]
242
+ end
243
+ Hash[pairs]
244
+ end
245
+
246
+ # @param [Sqreen::AggregatedMetric] agg
247
+ # @param [Hash] attrs
248
+ def conv_generic_metric(agg, attrs)
249
+ attrs[:payload] = Kit::Signals::Specialized::AggregatedMetric::Payload.new(
250
+ kind: metric_kind(agg.metric),
251
+ capture_interval_s: agg.metric.period,
252
+ date_started: agg.start,
253
+ date_ended: agg.finish,
254
+ values: agg.data
255
+ )
256
+
257
+ Kit::Signals::Specialized::AggregatedMetric.new(attrs)
258
+ end
259
+
260
+ # @param [Sqreen::AggregatedMetric] agg
261
+ # @param [Hash] attrs
262
+ def conv_binning_metric(agg, attrs)
263
+ attrs[:payload] = Kit::Signals::Specialized::BinningMetric::Payload.new(
264
+ capture_interval_s: agg.metric.period,
265
+ date_started: agg.start,
266
+ date_ended: agg.finish,
267
+ base: agg.data['b'],
268
+ unit: agg.data['u'],
269
+ max: agg.data['v']['max'],
270
+ bins: agg.data['v'].reject { |k, _v| k == 'max' }
271
+ )
272
+
273
+ Kit::Signals::Specialized::BinningMetric.new(attrs)
274
+ end
275
+
276
+ # @param [Sqreen::Metric::Base] metric
277
+ def metric_kind(metric)
278
+ metric.class.name.sub(/.*::/, '').sub(/Metric$/, '')
279
+ end
280
+ end
281
+ end
282
+ end
283
+ end