sqreen 1.19.3-java → 1.21.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 (113) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +38 -0
  3. data/lib/sqreen/actions/block_user.rb +1 -1
  4. data/lib/sqreen/actions/redirect_ip.rb +1 -1
  5. data/lib/sqreen/actions/redirect_user.rb +1 -1
  6. data/lib/sqreen/agent_message.rb +20 -0
  7. data/lib/sqreen/aggregated_metric.rb +25 -0
  8. data/lib/sqreen/attack_detected.html +1 -2
  9. data/lib/sqreen/ca.crt +24 -0
  10. data/lib/sqreen/condition_evaluator.rb +8 -2
  11. data/lib/sqreen/configuration.rb +11 -5
  12. data/lib/sqreen/deferred_logger.rb +50 -14
  13. data/lib/sqreen/deliveries/batch.rb +12 -2
  14. data/lib/sqreen/deliveries/simple.rb +4 -0
  15. data/lib/sqreen/deprecation.rb +38 -0
  16. data/lib/sqreen/ecosystem.rb +123 -0
  17. data/lib/sqreen/ecosystem/databases/database_connection_data.rb +23 -0
  18. data/lib/sqreen/ecosystem/databases/mongo.rb +39 -0
  19. data/lib/sqreen/ecosystem/databases/mysql.rb +54 -0
  20. data/lib/sqreen/ecosystem/databases/postgres.rb +51 -0
  21. data/lib/sqreen/ecosystem/databases/redis.rb +36 -0
  22. data/lib/sqreen/ecosystem/dispatch_table.rb +43 -0
  23. data/lib/sqreen/ecosystem/exception_reporting.rb +28 -0
  24. data/lib/sqreen/ecosystem/http/net_http.rb +50 -0
  25. data/lib/sqreen/ecosystem/http/rack_request.rb +39 -0
  26. data/lib/sqreen/ecosystem/loggable.rb +13 -0
  27. data/lib/sqreen/ecosystem/messaging/bunny.rb +61 -0
  28. data/lib/sqreen/ecosystem/messaging/kafka.rb +70 -0
  29. data/lib/sqreen/ecosystem/messaging/kinesis.rb +66 -0
  30. data/lib/sqreen/ecosystem/messaging/sqs.rb +68 -0
  31. data/lib/sqreen/ecosystem/module_api.rb +30 -0
  32. data/lib/sqreen/ecosystem/module_api/event_listener.rb +18 -0
  33. data/lib/sqreen/ecosystem/module_api/instrumentation.rb +23 -0
  34. data/lib/sqreen/ecosystem/module_api/message_producer.rb +57 -0
  35. data/lib/sqreen/ecosystem/module_api/signal_producer.rb +24 -0
  36. data/lib/sqreen/ecosystem/module_api/tracing.rb +45 -0
  37. data/lib/sqreen/ecosystem/module_api/tracing/client_data.rb +31 -0
  38. data/lib/sqreen/ecosystem/module_api/tracing/consumer_data.rb +13 -0
  39. data/lib/sqreen/ecosystem/module_api/tracing/messaging_data.rb +35 -0
  40. data/lib/sqreen/ecosystem/module_api/tracing/producer_data.rb +13 -0
  41. data/lib/sqreen/ecosystem/module_api/tracing/server_data.rb +27 -0
  42. data/lib/sqreen/ecosystem/module_api/tracing_id_generation.rb +16 -0
  43. data/lib/sqreen/ecosystem/module_api/transaction_storage.rb +71 -0
  44. data/lib/sqreen/ecosystem/module_registry.rb +48 -0
  45. data/lib/sqreen/ecosystem/tracing/modules/client.rb +35 -0
  46. data/lib/sqreen/ecosystem/tracing/modules/consumer.rb +35 -0
  47. data/lib/sqreen/ecosystem/tracing/modules/determine_ip.rb +28 -0
  48. data/lib/sqreen/ecosystem/tracing/modules/producer.rb +35 -0
  49. data/lib/sqreen/ecosystem/tracing/modules/server.rb +30 -0
  50. data/lib/sqreen/ecosystem/tracing/sampler.rb +160 -0
  51. data/lib/sqreen/ecosystem/tracing/sampling_configuration.rb +150 -0
  52. data/lib/sqreen/ecosystem/tracing/signals/tracing_client.rb +53 -0
  53. data/lib/sqreen/ecosystem/tracing/signals/tracing_consumer.rb +56 -0
  54. data/lib/sqreen/ecosystem/tracing/signals/tracing_producer.rb +56 -0
  55. data/lib/sqreen/ecosystem/tracing/signals/tracing_server.rb +53 -0
  56. data/lib/sqreen/ecosystem/tracing_broker.rb +101 -0
  57. data/lib/sqreen/ecosystem/tracing_id_setup.rb +34 -0
  58. data/lib/sqreen/ecosystem/transaction_storage.rb +64 -0
  59. data/lib/sqreen/ecosystem/util/call_writers_from_init.rb +13 -0
  60. data/lib/sqreen/ecosystem_integration.rb +81 -0
  61. data/lib/sqreen/ecosystem_integration/around_callbacks.rb +89 -0
  62. data/lib/sqreen/ecosystem_integration/instrumentation_service.rb +38 -0
  63. data/lib/sqreen/ecosystem_integration/request_lifecycle_tracking.rb +58 -0
  64. data/lib/sqreen/ecosystem_integration/signal_consumption.rb +35 -0
  65. data/lib/sqreen/endpoint_testing.rb +184 -0
  66. data/lib/sqreen/event.rb +7 -5
  67. data/lib/sqreen/events/attack.rb +23 -18
  68. data/lib/sqreen/events/remote_exception.rb +0 -22
  69. data/lib/sqreen/events/request_record.rb +15 -71
  70. data/lib/sqreen/frameworks/generic.rb +24 -1
  71. data/lib/sqreen/frameworks/rails.rb +0 -7
  72. data/lib/sqreen/frameworks/request_recorder.rb +15 -2
  73. data/lib/sqreen/graft/call.rb +85 -18
  74. data/lib/sqreen/graft/callback.rb +1 -1
  75. data/lib/sqreen/graft/hook.rb +192 -88
  76. data/lib/sqreen/graft/hook_point.rb +18 -11
  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 +59 -0
  83. data/lib/sqreen/legacy/instrumentation.rb +22 -10
  84. data/lib/sqreen/legacy/old_event_submission_strategy.rb +228 -0
  85. data/lib/sqreen/legacy/waf_redactions.rb +49 -0
  86. data/lib/sqreen/log.rb +3 -2
  87. data/lib/sqreen/log/loggable.rb +2 -1
  88. data/lib/sqreen/logger.rb +24 -0
  89. data/lib/sqreen/metrics/base.rb +3 -0
  90. data/lib/sqreen/metrics_store.rb +33 -12
  91. data/lib/sqreen/null_logger.rb +22 -0
  92. data/lib/sqreen/performance_notifications/binned_metrics.rb +8 -2
  93. data/lib/sqreen/remote_command.rb +4 -0
  94. data/lib/sqreen/rules.rb +12 -6
  95. data/lib/sqreen/rules/blacklist_ips_cb.rb +2 -2
  96. data/lib/sqreen/rules/custom_error_cb.rb +3 -3
  97. data/lib/sqreen/rules/rule_cb.rb +4 -0
  98. data/lib/sqreen/rules/waf_cb.rb +14 -11
  99. data/lib/sqreen/runner.rb +122 -15
  100. data/lib/sqreen/sensitive_data_redactor.rb +19 -31
  101. data/lib/sqreen/session.rb +53 -43
  102. data/lib/sqreen/signals/conversions.rb +288 -0
  103. data/lib/sqreen/signals/http_trace_redaction.rb +111 -0
  104. data/lib/sqreen/signals/signals_submission_strategy.rb +78 -0
  105. data/lib/sqreen/version.rb +1 -1
  106. data/lib/sqreen/weave/budget.rb +46 -0
  107. data/lib/sqreen/weave/legacy/instrumentation.rb +194 -103
  108. data/lib/sqreen/worker.rb +6 -2
  109. metadata +96 -7
  110. data/lib/sqreen/backport.rb +0 -9
  111. data/lib/sqreen/backport/clock_gettime.rb +0 -74
  112. data/lib/sqreen/backport/original_name.rb +0 -88
  113. data/lib/sqreen/encoding_sanitizer.rb +0 -27
@@ -0,0 +1,38 @@
1
+ require 'sqreen/graft/hook'
2
+ require 'sqreen/ecosystem_integration/around_callbacks'
3
+
4
+ module Sqreen
5
+ class EcosystemIntegration
6
+ module InstrumentationService
7
+ class << self
8
+ # @param [String] module_name
9
+ # @param [String] method in form A::B#c or A::B.c
10
+ # @param [Hash{Symbol=>Proc}] spec
11
+ def instrument(module_name, method, spec)
12
+ hook = Sqreen::Graft::Hook[method].add do
13
+ if spec[:before]
14
+ cb = AroundCallbacks.wrap_instrumentation_hook(module_name, 'pre', spec[:before])
15
+ before(nil, flow: true, &cb)
16
+ end
17
+
18
+ if spec[:after]
19
+ cb = AroundCallbacks.wrap_instrumentation_hook(module_name, 'post', spec[:after])
20
+ after(nil, flow: true, &cb)
21
+ end
22
+
23
+ if spec[:raised]
24
+ cb = AroundCallbacks.wrap_instrumentation_hook(module_name, 'failing', spec[:raised])
25
+ raised(nil, flow: true, &cb)
26
+ end
27
+
28
+ if spec[:ensured]
29
+ cb = AroundCallbacks.wrap_instrumentation_hook(module_name, 'finally', spec[:ensured])
30
+ ensured(nil, flow: true, &cb)
31
+ end
32
+ end
33
+ hook.install
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,58 @@
1
+ require 'sqreen/events/remote_exception'
2
+ require 'sqreen/log/loggable'
3
+
4
+ module Sqreen
5
+ class EcosystemIntegration
6
+ # This class gets notified of request start/end and
7
+ # 1) distributes such events to listeners (typically ecosystem modules;
8
+ # the method add_start_observer is exposed to ecosystem modules through
9
+ # +Sqreen::Ecosystem::ModuleApi::EventListener+ and the dispatch table).
10
+ # 2) keeps track of whether a request is active on this thread. This is
11
+ # so that users of this class can have this information without needing
12
+ # to subscribe to request start/events and keeping thread local state
13
+ # themselves.
14
+ # XXX: Since the Ecosystem is also notified of request start/end, it could
15
+ # notify its modules of request start without going through the dispatch
16
+ # table and call add_start_observer. We need to think if we want to keep
17
+ # the transaction / request distinction or if they should just be
18
+ # assumed to be the same, though.
19
+ class RequestLifecycleTracking
20
+ include Sqreen::Log::Loggable
21
+
22
+ def initialize
23
+ @start_observers = []
24
+ @tl_key = "#{object_id}_req_in_flight"
25
+ end
26
+
27
+ # API for classes needing to know the request state
28
+
29
+ # @param cb A callback taking a Rack::Request
30
+ def add_start_observer(cb)
31
+ @start_observers << cb
32
+ end
33
+
34
+ def in_request?
35
+ Thread.current[@tl_key] ? true : false
36
+ end
37
+
38
+ # API for classes notifying this one of request events
39
+
40
+ def notify_request_start(rack_req)
41
+ Thread.current[@tl_key] = true
42
+ return if @start_observers.empty?
43
+ @start_observers.each do |cb|
44
+ begin
45
+ cb.call(rack_req)
46
+ rescue ::Exception => e # rubocop:disable Lint/RescueException
47
+ logger.warn { "Error calling #{cb} on request start: #{e.message}" }
48
+ Sqreen::RemoteException.record(e)
49
+ end
50
+ end
51
+ end
52
+
53
+ def notify_request_end
54
+ Thread.current[@tl_key] = false
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,35 @@
1
+ require 'sqreen/log/loggable'
2
+
3
+ module Sqreen
4
+ class EcosystemIntegration
5
+ class SignalConsumption
6
+ include Sqreen::Log::Loggable
7
+
8
+ PAYLOAD_CREATOR_SECTIONS = %w[request response params headers].freeze
9
+
10
+ # @param [Sqreen::Frameworks::GenericFramework] framework
11
+ # @param [Sqreen::EcosystemIntegration::RequestLifecycleTracking]
12
+ # @param [Sqreen::CappedQueue]
13
+ def initialize(framework, req_lifecycle, queue)
14
+ @framework = framework
15
+ @req_lifecycle = req_lifecycle
16
+ @queue = queue
17
+ end
18
+
19
+ def consume_signal(signal)
20
+ # transitional
21
+ unless Sqreen.features.fetch('use_signals', DEFAULT_USE_SIGNALS)
22
+ logger.debug { "Discarding signal #{signal} (signals disabled)" }
23
+ return
24
+ end
25
+
26
+ if @req_lifecycle.in_request?
27
+ # add it to the request record
28
+ @framework.observe(:signals, signal, PAYLOAD_CREATOR_SECTIONS, true)
29
+ else
30
+ @queue.push signal
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,184 @@
1
+ require 'net/https'
2
+ require 'sqreen/agent_message'
3
+ require 'sqreen/log/loggable'
4
+
5
+ module Sqreen
6
+ class EndpointTesting
7
+ Endpoint = Struct.new(:url, :ca_store)
8
+ class ChosenEndpoints
9
+ def initialize
10
+ @messages = []
11
+ end
12
+
13
+ # @return [Sqreen::EndpointTesting::Endpoint]
14
+ attr_accessor :control
15
+
16
+ # @return [Sqreen::EndpointTesting::Endpoint]
17
+ attr_accessor :ingestion
18
+
19
+ # @return [Array<Sqreen::AgentMessage>]
20
+ attr_reader :messages
21
+
22
+ # @param [Sqreen::AgentMessage] message
23
+ def add_message(message)
24
+ @messages << message
25
+ end
26
+ end
27
+
28
+ MAIN_CONTROL_HOST = 'back.sqreen.com'.freeze
29
+ MAIN_INJECTION_HOST = 'ingestion.sqreen.com'.freeze
30
+ FALLBACK_ENDPOINT_URL = 'https://back.sqreen.io/'.freeze
31
+ GLOBAL_TIMEOUT = 30
32
+
33
+ CONTROL_ERROR_KIND = 'back_sqreen_com_unavailable'.freeze
34
+ INGESTION_ERROR_KIND = 'ingestion_sqreen_com_unavailable'.freeze
35
+
36
+ class << self
37
+ include Log::Loggable
38
+
39
+ # reproduces behaviour before endpoint testing was introduced
40
+ def no_test_endpoints(config_url, config_ingestion_url)
41
+ endpoints = ChosenEndpoints.new
42
+
43
+ endpoints.control = Endpoint.new(
44
+ config_url || "https://#{MAIN_CONTROL_HOST}/", cert_store
45
+ )
46
+ endpoints.ingestion = Endpoint.new(
47
+ config_ingestion_url || "https://#{MAIN_INJECTION_HOST}/", nil
48
+ )
49
+
50
+ endpoints
51
+ end
52
+
53
+ def test_endpoints(proxy_url, config_url, config_ingestion_url)
54
+ proxy_params = create_proxy_params(proxy_url)
55
+
56
+ # execute the tests in separate threads and wait for them
57
+ thread_control = Thread.new do
58
+ thread_main(config_url, proxy_params, MAIN_CONTROL_HOST)
59
+ end
60
+ thread_injection = Thread.new do
61
+ thread_main(config_ingestion_url, proxy_params, MAIN_INJECTION_HOST)
62
+ end
63
+
64
+ wait_for_threads(thread_control, thread_injection)
65
+
66
+ # build and return result
67
+ fallback = Endpoint.new(FALLBACK_ENDPOINT_URL, cert_store)
68
+ endpoints = ChosenEndpoints.new
69
+ endpoints.control = thread_control[:endpoint] || fallback
70
+ endpoints.ingestion = thread_injection[:endpoint] || fallback
71
+
72
+ if thread_control[:endpoint_error]
73
+ msg = AgentMessage.new(CONTROL_ERROR_KIND, thread_control[:endpoint_error])
74
+ endpoints.add_message msg
75
+ end
76
+ if thread_injection[:endpoint_error]
77
+ msg = AgentMessage.new(INGESTION_ERROR_KIND, thread_injection[:endpoint_error])
78
+ endpoints.add_message msg
79
+ end
80
+
81
+ endpoints
82
+ end
83
+
84
+ private
85
+
86
+ def thread_main(configured_url, proxy_params, host)
87
+ res = if configured_url
88
+ Endpoint.new(configured_url, nil)
89
+ else
90
+ EndpointTesting.send(:test_with_store_variants, proxy_params, host)
91
+ end
92
+
93
+ Thread.current[:endpoint] = res
94
+ rescue StandardError => e
95
+ Thread.current[:endpoint_error] = e.message
96
+ end
97
+
98
+ def create_proxy_params(proxy_url)
99
+ return [] unless proxy_url
100
+
101
+ proxy = URI.parse(proxy_url)
102
+
103
+ return [] unless proxy.scheme == 'http'
104
+
105
+ [proxy.host, proxy.port, proxy.user, proxy.password]
106
+ end
107
+
108
+ def test_with_store_variants(proxy_params, server_name)
109
+ # first without custom store
110
+ do_test(proxy_params, server_name, false)
111
+ rescue StandardError => _e
112
+ do_test(proxy_params, server_name, true)
113
+ end
114
+
115
+ # @param [Array] proxy_params
116
+ # @param [String] server_name
117
+ # @param [Boolean] custom_store
118
+ def do_test(proxy_params, server_name, custom_store)
119
+ http = Net::HTTP.new(server_name, 443, *proxy_params)
120
+ http.use_ssl = true
121
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE if ENV['SQREEN_SSL_NO_VERIFY']
122
+ http.verify_callback = lambda do |preverify_ok, ctx|
123
+ unless preverify_ok
124
+ logger.warn do
125
+ "Certificate validation failure for certificate issued to " \
126
+ "#{ctx.chain[0].subject}: #{ctx.error_string}"
127
+ end
128
+ end
129
+
130
+ preverify_ok
131
+ end
132
+
133
+ http.open_timeout = 13
134
+ http.ssl_timeout = 7
135
+ http.read_timeout = 7
136
+ http.close_on_empty_response = true
137
+
138
+ http.cert_store = cert_store if custom_store
139
+
140
+ resp = http.get('/ping')
141
+
142
+ logger.info do
143
+ "Got response from #{server_name}'s ping endpoint. " \
144
+ "Status code is #{resp.code} (custom CA store: #{custom_store})"
145
+ end
146
+
147
+ unless resp.code == '200'
148
+ raise "Response code for /ping is #{resp.code}, not 200"
149
+ end
150
+
151
+ Endpoint.new("https://#{server_name}/", http.cert_store)
152
+ rescue StandardError => e
153
+ logger.info do
154
+ "Error in request to #{server_name} " \
155
+ "(custom store: #{custom_store}): #{e.message}"
156
+ end
157
+
158
+ raise "Error in request to #{server_name}: #{e.message}"
159
+ end
160
+
161
+ def wait_for_threads(thread_control, thread_injection)
162
+ deadline = Time.now + GLOBAL_TIMEOUT
163
+ [thread_control, thread_injection].each do |thread|
164
+ rem = deadline - Time.now
165
+ rem = 0.1 if rem < 0.1
166
+ next if thread.join(rem)
167
+ logger.debug { "Timeout for thread #{thread}" }
168
+ thread.kill
169
+ thread[:endpoint_error] = "Timeout doing endpoint testing"
170
+ end
171
+ end
172
+
173
+ def cert_store
174
+ @cert_store ||= begin
175
+ cert_file = File.join(File.dirname(__FILE__), 'ca.crt')
176
+ cert_store = OpenSSL::X509::Store.new
177
+ cert_store.add_file cert_file
178
+
179
+ cert_store
180
+ end
181
+ end
182
+ end
183
+ end
184
+ end
@@ -8,17 +8,19 @@
8
8
  module Sqreen
9
9
  # Master interface for point in time events (e.g. Attack, RemoteException)
10
10
  class Event
11
+ # @return [Hash]
11
12
  attr_reader :payload
13
+
14
+ # @return [Time]
15
+ attr_accessor :time # writer used only in tests
16
+
12
17
  def initialize(payload)
13
18
  @payload = payload
14
- end
15
-
16
- def to_hash
17
- payload.to_hash
19
+ @time = Time.now.utc
18
20
  end
19
21
 
20
22
  def to_s
21
- "<#{self.class.name}: #{to_hash}>"
23
+ "<#{self.class.name}: #{payload.to_hash}>"
22
24
  end
23
25
  end
24
26
  end
@@ -11,6 +11,8 @@ module Sqreen
11
11
  # Attack
12
12
  # When creating a new attack, it gets automatically pushed to the event's
13
13
  # queue.
14
+ # XXX: TURNS OUT THIS CLASS IS ACTUALLY NOT USED ANYMORE
15
+ # Framework.observe is used instead with unstructured attack details
14
16
  class Attack < Event
15
17
  def self.record(payload)
16
18
  attack = Attack.new(payload)
@@ -26,11 +28,31 @@ module Sqreen
26
28
  payload['rule']['rulespack_id']
27
29
  end
28
30
 
29
- def type
31
+ def rule_name
30
32
  return nil unless payload['rule']
31
33
  payload['rule']['name']
32
34
  end
33
35
 
36
+ def test?
37
+ return nil unless payload['rule']
38
+ payload['rule']['test'] ? true : false
39
+ end
40
+
41
+ def beta?
42
+ return nil unless payload['rule']
43
+ payload['rule']['beta'] ? true : false
44
+ end
45
+
46
+ def block?
47
+ return nil unless payload['rule']
48
+ payload['rule']['block'] ? true : false
49
+ end
50
+
51
+ def attack_type
52
+ return nil unless payload['rule']
53
+ payload['rule']['attack_type']
54
+ end
55
+
34
56
  def time
35
57
  return nil unless payload['local']
36
58
  payload['local']['time']
@@ -44,22 +66,5 @@ module Sqreen
44
66
  def enqueue
45
67
  Sqreen.queue.push(self)
46
68
  end
47
-
48
- def to_hash
49
- res = {}
50
- rule_p = payload['rule']
51
- request_p = payload['request']
52
- res[:rule_name] = rule_p['name'] if rule_p && rule_p['name']
53
- res[:rulespack_id] = rule_p['rulespack_id'] if rule_p && rule_p['rulespack_id']
54
- res[:test] = rule_p['test'] if rule_p && rule_p['test']
55
- res[:infos] = payload['infos'] if payload['infos']
56
- res[:time] = time if time
57
- res[:client_ip] = request_p[:addr] if request_p && request_p[:addr]
58
- res[:request] = request_p if request_p
59
- res[:params] = payload['params'] if payload['params']
60
- res[:context] = payload['context'] if payload['context']
61
- res[:headers] = payload['headers'] if payload['headers']
62
- res
63
- end
64
69
  end
65
70
  end
@@ -30,27 +30,5 @@ module Sqreen
30
30
  def klass
31
31
  payload['exception'].class.name
32
32
  end
33
-
34
- def to_hash
35
- exception = payload['exception']
36
- ev = {
37
- :klass => exception.class.name,
38
- :message => exception.message,
39
- :params => payload['request_params'],
40
- :time => payload['time'],
41
- :infos => {
42
- :client_ip => payload['client_ip'],
43
- },
44
- :request => payload['request_infos'],
45
- :headers => payload['headers'],
46
- :rule_name => payload['rule_name'],
47
- :rulespack_id => payload['rulespack_id'],
48
- }
49
-
50
- ev[:infos].merge!(payload['infos']) if payload['infos']
51
- return ev unless exception.backtrace
52
- ev[:context] = { :backtrace => exception.backtrace.map(&:to_s) }
53
- ev
54
- end
55
33
  end
56
34
  end
@@ -8,12 +8,15 @@
8
8
  require 'json'
9
9
  require 'sqreen/log'
10
10
  require 'sqreen/event'
11
- require 'sqreen/encoding_sanitizer'
12
11
  require 'sqreen/sensitive_data_redactor'
13
12
 
14
13
  module Sqreen
15
14
  # When a request is deeemed worthy of being sent to the backend
16
15
  class RequestRecord < Sqreen::Event
16
+ attr_reader :redactor
17
+
18
+ # @param [Hash] payload
19
+ # @param [Sqreen::SensitiveDataRedactor] redactor
17
20
  def initialize(payload, redactor = nil)
18
21
  @redactor = redactor
19
22
  super(payload)
@@ -23,74 +26,18 @@ module Sqreen
23
26
  (payload && payload[:observed]) || {}
24
27
  end
25
28
 
26
- def to_hash
27
- res = { :version => '20171208' }
28
- if payload[:observed]
29
- res[:observed] = payload[:observed].dup
30
- rulespack = nil
31
- if observed[:attacks]
32
- res[:observed][:attacks] = observed[:attacks].map do |att|
33
- natt = att.dup
34
- rulespack = natt.delete(:rulespack_id) || rulespack
35
- natt
36
- end
37
- end
38
- if observed[:sqreen_exceptions]
39
- res[:observed][:sqreen_exceptions] = observed[:sqreen_exceptions].map do |exc|
40
- nex = exc.dup
41
- excp = nex.delete(:exception)
42
- if excp
43
- nex[:message] = excp.message
44
- nex[:klass] = excp.class.name
45
- end
46
- rulespack = nex.delete(:rulespack_id) || rulespack
47
- nex
48
- end
49
- end
50
- res[:rulespack_id] = rulespack unless rulespack.nil?
51
- if observed[:observations]
52
- res[:observed][:observations] = observed[:observations].map do |cat, key, value, time|
53
- { :category => cat, :key => key, :value => value, :time => time }
54
- end
55
- end
56
- if observed[:sdk]
57
- res[:observed][:sdk] = processed_sdk_calls
58
- end
59
- end
60
- res[:local] = payload['local'] if payload['local']
61
- if payload['request']
62
- res[:request] = payload['request'].dup
63
- res[:client_ip] = res[:request].delete(:client_ip) if res[:request][:client_ip]
64
- else
65
- res[:request] = {}
66
- end
67
- if payload['response']
68
- res[:response] = payload['response'].dup
69
- else
70
- res[:response] = {}
71
- end
72
-
73
- res[:request][:parameters] = payload['params'] if payload['params']
74
- res[:request][:headers] = payload['headers'] if payload['headers']
75
-
76
- res = Sqreen::EncodingSanitizer.sanitize(res)
29
+ def last_identify_args
30
+ return nil unless observed[:sdk]
77
31
 
78
- if @redactor
79
- res[:request], redacted = @redactor.redact(res[:request])
80
- if redacted.any? && res[:observed] && res[:observed][:attacks]
81
- res[:observed][:attacks] = @redactor.redact_attacks!(res[:observed][:attacks], redacted)
82
- end
83
- if redacted.any? && res[:observed] && res[:observed][:sqreen_exceptions]
84
- res[:observed][:sqreen_exceptions] = @redactor.redact_exceptions!(res[:observed][:sqreen_exceptions], redacted)
85
- end
32
+ observed[:sdk].reverse_each do |meth, _time, *args|
33
+ next unless meth == :identify
34
+ return args
86
35
  end
87
-
88
- res
36
+ nil
89
37
  end
90
38
 
91
- private
92
-
93
39
  def processed_sdk_calls
40
+ return [] unless observed[:sdk]
94
41
  auth_keys = last_identify_id
95
42
 
96
43
  observed[:sdk].map do |meth, time, *args|
@@ -102,6 +49,8 @@ module Sqreen
102
49
  end
103
50
  end
104
51
 
52
+ private
53
+
105
54
  def inject_identifiers(args, meth, auth_keys)
106
55
  return args unless meth == :track && auth_keys
107
56
 
@@ -118,13 +67,8 @@ module Sqreen
118
67
  end
119
68
 
120
69
  def last_identify_id
121
- return nil unless observed[:sdk]
122
-
123
- observed[:sdk].reverse_each do |meth, _time, *args|
124
- next unless meth == :identify
125
- return args.first if args.respond_to? :first
126
- end
127
- nil
70
+ args = last_identify_args
71
+ args.first if args.respond_to? :first
128
72
  end
129
73
  end
130
74
  end