sqreen 1.19.1 → 1.21.0.beta1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -0
- data/lib/sqreen/agent_message.rb +20 -0
- data/lib/sqreen/aggregated_metric.rb +25 -0
- data/lib/sqreen/attack_detected.html +1 -2
- data/lib/sqreen/ca.crt +24 -0
- data/lib/sqreen/configuration.rb +10 -4
- data/lib/sqreen/deliveries/batch.rb +12 -2
- data/lib/sqreen/deliveries/simple.rb +4 -0
- data/lib/sqreen/ecosystem.rb +80 -0
- data/lib/sqreen/ecosystem/dispatch_table.rb +43 -0
- data/lib/sqreen/ecosystem/http/net_http.rb +51 -0
- data/lib/sqreen/ecosystem/http/rack_request.rb +38 -0
- data/lib/sqreen/ecosystem/loggable.rb +13 -0
- data/lib/sqreen/ecosystem/module_api.rb +30 -0
- data/lib/sqreen/ecosystem/module_api/event_listener.rb +18 -0
- data/lib/sqreen/ecosystem/module_api/instrumentation.rb +23 -0
- data/lib/sqreen/ecosystem/module_api/signal_producer.rb +26 -0
- data/lib/sqreen/ecosystem/module_api/tracing_push_down.rb +34 -0
- data/lib/sqreen/ecosystem/module_api/transaction_storage.rb +71 -0
- data/lib/sqreen/ecosystem/module_registry.rb +39 -0
- data/lib/sqreen/ecosystem/redis/redis_connection.rb +35 -0
- data/lib/sqreen/ecosystem/tracing/sampler.rb +160 -0
- data/lib/sqreen/ecosystem/tracing/sampling_configuration.rb +150 -0
- data/lib/sqreen/ecosystem/tracing/signals/tracing_client.rb +53 -0
- data/lib/sqreen/ecosystem/tracing/signals/tracing_server.rb +53 -0
- data/lib/sqreen/ecosystem/tracing_id_setup.rb +34 -0
- data/lib/sqreen/ecosystem/transaction_storage.rb +64 -0
- data/lib/sqreen/ecosystem_integration.rb +70 -0
- data/lib/sqreen/ecosystem_integration/around_callbacks.rb +89 -0
- data/lib/sqreen/ecosystem_integration/instrumentation_service.rb +38 -0
- data/lib/sqreen/ecosystem_integration/request_lifecycle_tracking.rb +56 -0
- data/lib/sqreen/ecosystem_integration/signal_consumption.rb +35 -0
- data/lib/sqreen/endpoint_testing.rb +184 -0
- data/lib/sqreen/event.rb +7 -5
- data/lib/sqreen/events/attack.rb +23 -18
- data/lib/sqreen/events/remote_exception.rb +0 -22
- data/lib/sqreen/events/request_record.rb +15 -70
- data/lib/sqreen/frameworks/generic.rb +15 -1
- data/lib/sqreen/frameworks/request_recorder.rb +13 -2
- data/lib/sqreen/graft/call.rb +9 -0
- data/lib/sqreen/kit/signals/specialized/aggregated_metric.rb +72 -0
- data/lib/sqreen/kit/signals/specialized/attack.rb +57 -0
- data/lib/sqreen/kit/signals/specialized/binning_metric.rb +76 -0
- data/lib/sqreen/kit/signals/specialized/http_trace.rb +26 -0
- data/lib/sqreen/kit/signals/specialized/sdk_track_call.rb +50 -0
- data/lib/sqreen/kit/signals/specialized/sqreen_exception.rb +57 -0
- data/lib/sqreen/legacy/old_event_submission_strategy.rb +227 -0
- data/lib/sqreen/legacy/waf_redactions.rb +49 -0
- data/lib/sqreen/log/loggable.rb +1 -1
- data/lib/sqreen/metrics/base.rb +3 -0
- data/lib/sqreen/metrics_store.rb +22 -12
- data/lib/sqreen/performance_notifications/binned_metrics.rb +8 -2
- data/lib/sqreen/remote_command.rb +3 -0
- data/lib/sqreen/rules.rb +4 -2
- data/lib/sqreen/rules/not_found_cb.rb +2 -0
- data/lib/sqreen/rules/rule_cb.rb +2 -0
- data/lib/sqreen/rules/waf_cb.rb +13 -10
- data/lib/sqreen/runner.rb +94 -13
- data/lib/sqreen/sensitive_data_redactor.rb +19 -31
- data/lib/sqreen/session.rb +53 -43
- data/lib/sqreen/signals/conversions.rb +288 -0
- data/lib/sqreen/signals/http_trace_redaction.rb +111 -0
- data/lib/sqreen/signals/signals_submission_strategy.rb +78 -0
- data/lib/sqreen/version.rb +1 -1
- data/lib/sqreen/weave/legacy/instrumentation.rb +4 -4
- metadata +74 -10
- data/lib/sqreen/backport.rb +0 -9
- data/lib/sqreen/backport/clock_gettime.rb +0 -74
- data/lib/sqreen/backport/original_name.rb +0 -88
@@ -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,56 @@
|
|
1
|
+
require 'sqreen/log/loggable'
|
2
|
+
|
3
|
+
module Sqreen
|
4
|
+
class EcosystemIntegration
|
5
|
+
# This class gets notified of request start/end and
|
6
|
+
# 1) distributes such events to listeners (typically ecosystem modules;
|
7
|
+
# the method add_start_observer is exposed to ecosystem modules through
|
8
|
+
# +Sqreen::Ecosystem::ModuleApi::EventListener+ and the dispatch table).
|
9
|
+
# 2) keeps track of whether a request is active on this thread. This is
|
10
|
+
# so that users of this class can have this information without needing
|
11
|
+
# to subscribe to request start/events and keeping thread local state
|
12
|
+
# themselves.
|
13
|
+
# XXX: Since the Ecosystem is also notified of request start/end, it could
|
14
|
+
# notify its modules of request start without going through the dispatch
|
15
|
+
# table and call add_start_observer. We need to think if we want to keep
|
16
|
+
# the transaction / request distinction or if they should just be
|
17
|
+
# assumed to be the same, though.
|
18
|
+
class RequestLifecycleTracking
|
19
|
+
include Sqreen::Log::Loggable
|
20
|
+
|
21
|
+
def initialize
|
22
|
+
@start_observers = []
|
23
|
+
@tl_key = "#{object_id}_req_in_flight"
|
24
|
+
end
|
25
|
+
|
26
|
+
# API for classes needing to know the request state
|
27
|
+
|
28
|
+
# @param cb A callback taking a Rack::Request
|
29
|
+
def add_start_observer(cb)
|
30
|
+
@start_observers << cb
|
31
|
+
end
|
32
|
+
|
33
|
+
def in_request?
|
34
|
+
Thread.current[@tl_key] ? true : false
|
35
|
+
end
|
36
|
+
|
37
|
+
# API for classes notifying this one of request events
|
38
|
+
|
39
|
+
def notify_request_start(rack_req)
|
40
|
+
Thread.current[@tl_key] = true
|
41
|
+
return if @start_observers.empty?
|
42
|
+
@start_observers.each do |cb|
|
43
|
+
begin
|
44
|
+
cb.call(rack_req)
|
45
|
+
rescue ::Exception => e # rubocop:disable Lint/RescueException
|
46
|
+
logger.warn { "Error calling #{cb} on request start: #{e.message}" }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def notify_request_end
|
52
|
+
Thread.current[@tl_key] = false
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
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
|
data/lib/sqreen/event.rb
CHANGED
@@ -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
|
-
|
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
|
data/lib/sqreen/events/attack.rb
CHANGED
@@ -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
|
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
|
@@ -14,6 +14,10 @@ require 'sqreen/sensitive_data_redactor'
|
|
14
14
|
module Sqreen
|
15
15
|
# When a request is deeemed worthy of being sent to the backend
|
16
16
|
class RequestRecord < Sqreen::Event
|
17
|
+
attr_reader :redactor
|
18
|
+
|
19
|
+
# @param [Hash] payload
|
20
|
+
# @param [Sqreen::SensitiveDataRedactor] redactor
|
17
21
|
def initialize(payload, redactor = nil)
|
18
22
|
@redactor = redactor
|
19
23
|
super(payload)
|
@@ -23,74 +27,18 @@ module Sqreen
|
|
23
27
|
(payload && payload[:observed]) || {}
|
24
28
|
end
|
25
29
|
|
26
|
-
def
|
27
|
-
|
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)
|
30
|
+
def last_identify_args
|
31
|
+
return nil unless observed[:sdk]
|
77
32
|
|
78
|
-
|
79
|
-
|
80
|
-
|
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
|
33
|
+
observed[:sdk].reverse_each do |meth, _time, *args|
|
34
|
+
next unless meth == :identify
|
35
|
+
return args
|
86
36
|
end
|
87
|
-
|
88
|
-
res
|
37
|
+
nil
|
89
38
|
end
|
90
39
|
|
91
|
-
private
|
92
|
-
|
93
40
|
def processed_sdk_calls
|
41
|
+
return [] unless observed[:sdk]
|
94
42
|
auth_keys = last_identify_id
|
95
43
|
|
96
44
|
observed[:sdk].map do |meth, time, *args|
|
@@ -102,6 +50,8 @@ module Sqreen
|
|
102
50
|
end
|
103
51
|
end
|
104
52
|
|
53
|
+
private
|
54
|
+
|
105
55
|
def inject_identifiers(args, meth, auth_keys)
|
106
56
|
return args unless meth == :track && auth_keys
|
107
57
|
|
@@ -118,13 +68,8 @@ module Sqreen
|
|
118
68
|
end
|
119
69
|
|
120
70
|
def last_identify_id
|
121
|
-
|
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
|
71
|
+
args = last_identify_args
|
72
|
+
args.first if args.respond_to? :first
|
128
73
|
end
|
129
74
|
end
|
130
75
|
end
|