sqreen 1.21.0.beta2 → 1.21.0.beta3
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 +15 -7
- data/lib/sqreen/actions/block_user.rb +1 -1
- data/lib/sqreen/actions/redirect_ip.rb +1 -1
- data/lib/sqreen/actions/redirect_user.rb +1 -1
- data/lib/sqreen/condition_evaluator.rb +9 -2
- data/lib/sqreen/conditionable.rb +24 -6
- data/lib/sqreen/configuration.rb +1 -1
- data/lib/sqreen/deferred_logger.rb +50 -14
- data/lib/sqreen/deprecation.rb +38 -0
- data/lib/sqreen/ecosystem_integration.rb +7 -1
- data/lib/sqreen/ecosystem_integration/around_callbacks.rb +20 -10
- data/lib/sqreen/ecosystem_integration/instrumentation_service.rb +8 -4
- data/lib/sqreen/events/request_record.rb +0 -1
- data/lib/sqreen/frameworks/generic.rb +9 -0
- data/lib/sqreen/frameworks/rails.rb +0 -7
- data/lib/sqreen/frameworks/request_recorder.rb +2 -0
- data/lib/sqreen/graft/call.rb +99 -21
- data/lib/sqreen/graft/callback.rb +1 -1
- data/lib/sqreen/graft/hook.rb +212 -100
- data/lib/sqreen/graft/hook_point.rb +18 -11
- data/lib/sqreen/legacy/instrumentation.rb +22 -10
- data/lib/sqreen/legacy/old_event_submission_strategy.rb +2 -1
- data/lib/sqreen/log.rb +3 -2
- data/lib/sqreen/log/loggable.rb +1 -0
- data/lib/sqreen/logger.rb +24 -0
- data/lib/sqreen/metrics.rb +1 -0
- data/lib/sqreen/metrics/req_detailed.rb +41 -0
- data/lib/sqreen/metrics_store.rb +11 -0
- data/lib/sqreen/null_logger.rb +22 -0
- data/lib/sqreen/remote_command.rb +1 -0
- data/lib/sqreen/rules.rb +8 -4
- data/lib/sqreen/rules/blacklist_ips_cb.rb +2 -2
- data/lib/sqreen/rules/custom_error_cb.rb +3 -3
- data/lib/sqreen/rules/rule_cb.rb +4 -2
- data/lib/sqreen/rules/waf_cb.rb +3 -3
- data/lib/sqreen/runner.rb +46 -5
- data/lib/sqreen/version.rb +1 -1
- data/lib/sqreen/weave/budget.rb +35 -0
- data/lib/sqreen/weave/legacy/instrumentation.rb +274 -132
- data/lib/sqreen/worker.rb +6 -2
- metadata +22 -6
- data/lib/sqreen/encoding_sanitizer.rb +0 -27
@@ -55,12 +55,12 @@ module Sqreen
|
|
55
55
|
end
|
56
56
|
|
57
57
|
def respond_page
|
58
|
-
page
|
58
|
+
@page ||= File.read(File.join(File.dirname(__FILE__), '../attack_detected.html'))
|
59
59
|
headers = {
|
60
60
|
'Content-Type' => 'text/html',
|
61
|
-
'Content-Length' => page.size.to_s,
|
61
|
+
'Content-Length' => @page.size.to_s,
|
62
62
|
}
|
63
|
-
[@status_code, headers, page]
|
63
|
+
[@status_code, headers, [@page]]
|
64
64
|
end
|
65
65
|
end
|
66
66
|
end
|
data/lib/sqreen/rules/rule_cb.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
# Copyright (c) 2015 Sqreen. All Rights Reserved.
|
4
4
|
# Please refer to our terms for more information: https://www.sqreen.com/terms.html
|
5
5
|
|
6
|
+
require 'sqreen/deprecation'
|
6
7
|
require 'sqreen/framework_cb'
|
7
8
|
require 'sqreen/context'
|
8
9
|
require 'sqreen/conditionable'
|
@@ -89,9 +90,9 @@ module Sqreen
|
|
89
90
|
framework.observe(:sqreen_exceptions, payload)
|
90
91
|
end
|
91
92
|
|
92
|
-
# Recommend taking an action (
|
93
|
+
# Recommend taking an action (optionally adding more data/context)
|
93
94
|
#
|
94
|
-
# This will format the requested action and
|
95
|
+
# This will format the requested action and optionally
|
95
96
|
# override it if it should not be taken (should not block for example)
|
96
97
|
def advise_action(action, additional_data = {})
|
97
98
|
return if action.nil? && additional_data.empty?
|
@@ -109,6 +110,7 @@ module Sqreen
|
|
109
110
|
)
|
110
111
|
true
|
111
112
|
end
|
113
|
+
Sqreen::Deprecation.deprecate(instance_method(:overtime!))
|
112
114
|
end
|
113
115
|
end
|
114
116
|
end
|
data/lib/sqreen/rules/waf_cb.rb
CHANGED
@@ -11,7 +11,7 @@ require 'sqreen/safe_json'
|
|
11
11
|
require 'sqreen/exception'
|
12
12
|
require 'sqreen/util/capper'
|
13
13
|
require 'sqreen/dependency/libsqreen'
|
14
|
-
require 'sqreen/
|
14
|
+
require 'sqreen/kit/string_sanitizer'
|
15
15
|
|
16
16
|
module Sqreen
|
17
17
|
module Rules
|
@@ -60,7 +60,7 @@ module Sqreen
|
|
60
60
|
end
|
61
61
|
|
62
62
|
# 0 for using defaults (PW_RUN_TIMEOUT)
|
63
|
-
@max_run_budget_us = (@data['values'].fetch('
|
63
|
+
@max_run_budget_us = (@data['values'].fetch('max_budget_ms', 0) * 1000).to_i
|
64
64
|
@max_run_budget_us = INFINITE_BUDGET_US if @max_run_budget_us >= INFINITE_BUDGET_US
|
65
65
|
|
66
66
|
Sqreen.log.debug { "Max WAF run budget for #{@waf_rule_name} set to #{@max_run_budget_us} us" }
|
@@ -82,7 +82,7 @@ module Sqreen
|
|
82
82
|
waf_args = binding_accessors.each_with_object({}) do |(e, b), h|
|
83
83
|
h[e] = capper.call(b.resolve(*env))
|
84
84
|
end
|
85
|
-
waf_args = Sqreen::
|
85
|
+
waf_args = Sqreen::Kit::StringSanitizer.sanitize(waf_args)
|
86
86
|
|
87
87
|
if budget
|
88
88
|
rem_budget_s = budget - (Sqreen.time - start)
|
data/lib/sqreen/runner.rb
CHANGED
@@ -141,7 +141,12 @@ module Sqreen
|
|
141
141
|
end
|
142
142
|
|
143
143
|
if @configuration.get(:weave) || needs_weave.call
|
144
|
-
|
144
|
+
# XXX: don't get updated
|
145
|
+
opts = {
|
146
|
+
perf_req_metrics_max_reqs: Sqreen.features['perf_req_metrics_max_reqs'],
|
147
|
+
perf_req_metrics_period: Sqreen.features['perf_req_metrics_period'],
|
148
|
+
}
|
149
|
+
@instrumenter = Sqreen::Weave::Legacy::Instrumentation.new(metrics_engine, opts)
|
145
150
|
else
|
146
151
|
@instrumenter = Sqreen::Legacy::Instrumentation.new(metrics_engine)
|
147
152
|
end
|
@@ -167,7 +172,9 @@ module Sqreen
|
|
167
172
|
end
|
168
173
|
self.features = wanted_features
|
169
174
|
|
170
|
-
@ecosystem_integration = EcosystemIntegration.new(framework,
|
175
|
+
@ecosystem_integration = EcosystemIntegration.new(framework,
|
176
|
+
Sqreen.queue,
|
177
|
+
create_binning_metric_proc)
|
171
178
|
framework.req_start_cb = @ecosystem_integration.method(:request_start)
|
172
179
|
framework.req_end_cb = @ecosystem_integration.method(:request_end)
|
173
180
|
|
@@ -274,7 +281,7 @@ module Sqreen
|
|
274
281
|
|
275
282
|
# XXX: ecosystem instrumentation should likely be deferred
|
276
283
|
# the same way the rest might be
|
277
|
-
@ecosystem_integration.init
|
284
|
+
@ecosystem_integration.init unless Sqreen.features['disable_ecosystem']
|
278
285
|
rulespack_id.to_s
|
279
286
|
end
|
280
287
|
|
@@ -394,8 +401,18 @@ module Sqreen
|
|
394
401
|
|
395
402
|
def change_performance_budget(budget, _context_infos = {})
|
396
403
|
return false unless budget.nil? || budget.to_f > 0
|
397
|
-
|
398
|
-
|
404
|
+
|
405
|
+
if @configuration.get(:weave)
|
406
|
+
prev = Sqreen::Weave::Budget.current
|
407
|
+
prev = prev.to_h if prev
|
408
|
+
|
409
|
+
budget_s = budget.to_f / 1000.0 if budget
|
410
|
+
Sqreen::Weave::Budget.update(threshold: budget_s)
|
411
|
+
else
|
412
|
+
prev = Sqreen.performance_budget
|
413
|
+
Sqreen.update_performance_budget(budget)
|
414
|
+
end
|
415
|
+
|
399
416
|
{ :was => prev }
|
400
417
|
end
|
401
418
|
|
@@ -492,6 +509,15 @@ module Sqreen
|
|
492
509
|
logout
|
493
510
|
end
|
494
511
|
|
512
|
+
def restart(_context_infos = {})
|
513
|
+
shutdown
|
514
|
+
heartbeat_delay = @heartbeat_delay
|
515
|
+
Thread.new do
|
516
|
+
sleep(2 * heartbeat_delay)
|
517
|
+
Sqreen::Worker.start(Sqreen.framework)
|
518
|
+
end
|
519
|
+
end
|
520
|
+
|
495
521
|
def logout(retrying = true)
|
496
522
|
return unless session
|
497
523
|
Sqreen.log.debug("Logging out")
|
@@ -529,6 +555,21 @@ module Sqreen
|
|
529
555
|
|
530
556
|
private
|
531
557
|
|
558
|
+
def create_binning_metric_proc
|
559
|
+
lambda do |metric_name|
|
560
|
+
return if @metrics_engine.metric?(metric_name)
|
561
|
+
metrics_engine.create_metric(
|
562
|
+
'name' => metric_name,
|
563
|
+
'kind' => 'Binning',
|
564
|
+
'period' => Sqreen.features['performance_metrics_period'] || 60,
|
565
|
+
'options' => {
|
566
|
+
'base' => Sqreen.features['perf_base'] || PerformanceNotifications::BinnedMetrics::DEFAULT_PERF_BASE,
|
567
|
+
'factor' => Sqreen.features['perf_unit'] || PerformanceNotifications::BinnedMetrics::DEFAULT_PERF_UNIT,
|
568
|
+
},
|
569
|
+
)
|
570
|
+
end
|
571
|
+
end
|
572
|
+
|
532
573
|
def post_endpoint_testing_msgs(chosen_endpoints)
|
533
574
|
chosen_endpoints.messages.each do |msg|
|
534
575
|
session.post_agent_message(@framework, msg)
|
data/lib/sqreen/version.rb
CHANGED
@@ -0,0 +1,35 @@
|
|
1
|
+
# typed: false
|
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
|
+
require 'sqreen/log/loggable'
|
7
|
+
require 'sqreen/weave'
|
8
|
+
|
9
|
+
class Sqreen::Weave::Budget
|
10
|
+
include Sqreen::Log::Loggable
|
11
|
+
|
12
|
+
def initialize(threshold)
|
13
|
+
@threshold = threshold
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_reader :threshold
|
17
|
+
|
18
|
+
def to_h
|
19
|
+
{ threshold: threshold }
|
20
|
+
end
|
21
|
+
|
22
|
+
class << self
|
23
|
+
attr_reader :current
|
24
|
+
|
25
|
+
def update(opts = nil)
|
26
|
+
Sqreen::Weave.logger.info("budget update:#{opts.inspect}")
|
27
|
+
|
28
|
+
return @current = nil if opts.nil? || opts.empty?
|
29
|
+
|
30
|
+
threshold = opts[:threshold]
|
31
|
+
|
32
|
+
@current = threshold
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -4,23 +4,41 @@
|
|
4
4
|
# Please refer to our terms for more information: https://www.sqreen.com/terms.html
|
5
5
|
|
6
6
|
require 'sqreen/weave/legacy'
|
7
|
+
require 'sqreen/weave/budget'
|
8
|
+
require 'sqreen/graft/hook'
|
7
9
|
require 'sqreen/graft/hook_point'
|
8
10
|
require 'sqreen/call_countable'
|
9
11
|
require 'sqreen/rules'
|
10
12
|
require 'sqreen/rules/record_request_context'
|
13
|
+
require 'sqreen/sqreen_signed_verifier'
|
14
|
+
require 'rack/request'
|
15
|
+
begin
|
16
|
+
require 'sq_detailed_metrics'
|
17
|
+
rescue LoadError => _e # rubocop:disable Lint/HandleExceptions
|
18
|
+
end
|
11
19
|
|
12
20
|
class Sqreen::Weave::Legacy::Instrumentation
|
13
21
|
attr_accessor :metrics_engine
|
14
22
|
|
23
|
+
HAS_SQ_DETAILED_METRICS = defined?(::SqDetailedMetrics)
|
24
|
+
REQ_LVL_2_METRIC = 'request_level_perf'.freeze
|
25
|
+
|
15
26
|
def initialize(metrics_engine, opts = {})
|
16
27
|
Sqreen::Weave.logger.debug { "#{self.class.name}#initialize #{metrics_engine}" }
|
17
28
|
@hooks = []
|
18
29
|
|
30
|
+
unless HAS_SQ_DETAILED_METRICS
|
31
|
+
Sqreen::Weave.logger.warn { "Detailed metrics are unavailable" }
|
32
|
+
end
|
33
|
+
|
19
34
|
self.metrics_engine = metrics_engine
|
20
35
|
|
21
36
|
### bail out if no metric engine
|
22
37
|
return if metrics_engine.nil?
|
23
38
|
|
39
|
+
# XXX: these metric definitions do not support change of opts
|
40
|
+
# due to features updates!
|
41
|
+
|
24
42
|
### init metric to count calls to sqreen
|
25
43
|
metrics_engine.create_metric(
|
26
44
|
'name' => 'sqreen_call_counts',
|
@@ -60,12 +78,42 @@ class Sqreen::Weave::Legacy::Instrumentation
|
|
60
78
|
'options' => opts[:perf_metric_percent] || { 'base' => 1.3, 'factor' => 1.0 },
|
61
79
|
)
|
62
80
|
|
81
|
+
metrics_engine.create_metric(
|
82
|
+
'name' => 'req.sq.hook.overhead',
|
83
|
+
'period' => 60,
|
84
|
+
'kind' => 'Binning',
|
85
|
+
'options' => { 'base' => 2.0, 'factor' => 0.1 },
|
86
|
+
)
|
87
|
+
|
88
|
+
metrics_engine.create_metric(
|
89
|
+
'name' => 'sq.hook.overhead',
|
90
|
+
'period' => 60,
|
91
|
+
'kind' => 'Binning',
|
92
|
+
'options' => { 'base' => 2.0, 'factor' => 0.1 },
|
93
|
+
)
|
94
|
+
|
95
|
+
metrics_engine.create_metric(
|
96
|
+
'name' => 'sq.shrinkwrap',
|
97
|
+
'period' => 60,
|
98
|
+
'kind' => 'Binning',
|
99
|
+
'options' => { 'base' => 2.0, 'factor' => 0.1 },
|
100
|
+
)
|
101
|
+
|
63
102
|
Sqreen.thread_cpu_time? && metrics_engine.create_metric(
|
64
103
|
'name' => 'sq_thread_cpu_pct',
|
65
104
|
'period' => opts[:period] || 60,
|
66
105
|
'kind' => 'Binning',
|
67
106
|
'options' => opts[:perf_metric_percent] || { 'base' => 1.3, 'factor' => 1.0 },
|
68
107
|
)
|
108
|
+
|
109
|
+
if HAS_SQ_DETAILED_METRICS # rubocop:disable Style/GuardClause
|
110
|
+
@lvl_2_metric = metrics_engine.create_metric(
|
111
|
+
'name' => REQ_LVL_2_METRIC,
|
112
|
+
'period' => opts[:perf_req_metrics_period] || 60,
|
113
|
+
'kind' => 'ReqDetailed',
|
114
|
+
)
|
115
|
+
@lvl_2_max_reqs = opts[:perf_req_metrics_max_reqs] || 100
|
116
|
+
end
|
69
117
|
end
|
70
118
|
|
71
119
|
# needed by Sqreen::Runner#initialize
|
@@ -84,6 +132,15 @@ class Sqreen::Weave::Legacy::Instrumentation
|
|
84
132
|
|
85
133
|
### set up rule signature verifier
|
86
134
|
verifier = nil
|
135
|
+
if Sqreen.features['rules_signature'] &&
|
136
|
+
Sqreen.config_get(:rules_verify_signature) == true &&
|
137
|
+
!defined?(::JRUBY_VERSION)
|
138
|
+
verifier = Sqreen::SqreenSignedVerifier.new
|
139
|
+
Sqreen::Weave.logger.debug('Rules signature enabled')
|
140
|
+
else
|
141
|
+
Sqreen::Weave.logger.debug('Rules signature disabled')
|
142
|
+
end
|
143
|
+
|
87
144
|
### force clean instrumentation callback list
|
88
145
|
@hooks = []
|
89
146
|
### for each rule description
|
@@ -94,6 +151,25 @@ class Sqreen::Weave::Legacy::Instrumentation
|
|
94
151
|
next unless rule_callback
|
95
152
|
### attach framework to callback
|
96
153
|
rule_callback.framework = framework
|
154
|
+
## create metric
|
155
|
+
Sqreen::Weave.logger.debug { "Adding rule metric: #{rule_callback}" }
|
156
|
+
[:pre, :post, :failing].each do |whence|
|
157
|
+
next unless rule_callback.send(:"#{whence}?")
|
158
|
+
metric_name = "sq.#{rule['name']}.#{whence}"
|
159
|
+
metrics_engine.create_metric(
|
160
|
+
'name' => metric_name,
|
161
|
+
'period' => 60,
|
162
|
+
'kind' => 'Binning',
|
163
|
+
'options' => { 'base' => 2.0, 'factor' => 0.1 },
|
164
|
+
)
|
165
|
+
metric_name = "req.sq.#{rule['name']}.#{whence}"
|
166
|
+
metrics_engine.create_metric(
|
167
|
+
'name' => metric_name,
|
168
|
+
'period' => 60,
|
169
|
+
'kind' => 'Binning',
|
170
|
+
'options' => { 'base' => 2.0, 'factor' => 0.1 },
|
171
|
+
)
|
172
|
+
end
|
97
173
|
### install callback, observing priority
|
98
174
|
Sqreen::Weave.logger.debug { "Adding rule callback: #{rule_callback}" }
|
99
175
|
@hooks << add_callback("weave,rule=#{rule['name']}", rule_callback, strategy)
|
@@ -107,30 +183,62 @@ class Sqreen::Weave::Legacy::Instrumentation
|
|
107
183
|
end
|
108
184
|
|
109
185
|
metrics_engine = self.metrics_engine
|
186
|
+
lvl_2_metric = @lvl_2_metric
|
187
|
+
lvl_2_max_reqs = @lvl_2_max_reqs
|
188
|
+
|
110
189
|
request_hook = Sqreen::Graft::Hook['Sqreen::ShrinkWrap#call', strategy]
|
111
190
|
@hooks << request_hook
|
112
191
|
request_hook.add do
|
113
|
-
before('wave,meta,request', rank: -100000, mandatory: true) do |
|
192
|
+
before('wave,meta,request', rank: -100000, mandatory: true) do |call|
|
114
193
|
next unless Sqreen.instrumentation_ready
|
115
194
|
|
116
|
-
|
117
|
-
|
195
|
+
# shrinkwrap_timer = Sqreen::Graft::Timer.new('weave,shrinkwrap')
|
196
|
+
# shrinkwrap_timer.start
|
197
|
+
|
198
|
+
request_timer = Sqreen::Graft::Timer.new("request")
|
199
|
+
request_timer.start
|
200
|
+
sqreen_timer = Sqreen::Graft::Timer.new("sqreen")
|
201
|
+
budget = Sqreen::Weave::Budget.current
|
202
|
+
|
203
|
+
timed_level = (Sqreen.features['perf_level'] || 1).to_i
|
204
|
+
timed_level = 1 if !HAS_SQ_DETAILED_METRICS && timed_level == 2
|
205
|
+
if timed_level == 2 && lvl_2_metric.num_requests >= lvl_2_max_reqs
|
206
|
+
timed_level = 1
|
207
|
+
Sqreen::Weave.logger.debug { "Reducing timed level to 1 (#{lvl_2_metric.num_requests} reqs accumulated)" }
|
208
|
+
end
|
209
|
+
|
210
|
+
Sqreen::Weave.logger.debug { "request budget: #{budget} timed.level: #{timed_level}" } if Sqreen::Weave.logger.debug?
|
211
|
+
|
212
|
+
route_found = nil
|
213
|
+
if timed_level >= 2
|
214
|
+
rack_env, = call.args
|
215
|
+
rack_request = Rack::Request.new(rack_env) if rack_env
|
216
|
+
|
217
|
+
# TODO: Rails engines
|
218
|
+
# TODO: Struct
|
219
|
+
# TODO: Sinatra
|
220
|
+
# TODO: Rack?
|
221
|
+
Rails.application.routes.router.recognize(rack_request) do |route, params|
|
222
|
+
route = ActionDispatch::Routing::RouteWrapper.new(route)
|
223
|
+
route_found = { name: route.name, verb: route.verb, path: route.path, reqs: route.reqs, params: params }
|
224
|
+
end if defined?(Rails) && Rails.application && defined?(ActionDispatch::Routing::RouteWrapper)
|
225
|
+
end
|
226
|
+
|
227
|
+
# TODO: Struct
|
118
228
|
Thread.current[:sqreen_http_request] = {
|
119
|
-
|
120
|
-
|
121
|
-
time_budget: Sqreen.performance_budget,
|
229
|
+
request_timer: request_timer,
|
230
|
+
sqreen_timer: sqreen_timer,
|
122
231
|
time_budget_expended: false,
|
123
|
-
|
232
|
+
time_budget: budget,
|
124
233
|
timed_callbacks: [],
|
125
234
|
timed_hooks: [],
|
126
|
-
|
127
|
-
timed_hooks_after: [],
|
128
|
-
timed_hooks_raised: [],
|
129
|
-
timed_hooks_ensured: [],
|
235
|
+
timed_level: timed_level,
|
130
236
|
skipped_callbacks: [],
|
237
|
+
route: ("#{route_found[:verb]} #{route_found[:path]}" if route_found),
|
238
|
+
# timed_shrinkwrap: shrinkwrap_timer,
|
131
239
|
}
|
132
240
|
|
133
|
-
|
241
|
+
# shrinkwrap_timer.stop
|
134
242
|
end
|
135
243
|
|
136
244
|
ensured('weave,meta,request', rank: 100000, mandatory: true) do |_call|
|
@@ -138,105 +246,89 @@ class Sqreen::Weave::Legacy::Instrumentation
|
|
138
246
|
|
139
247
|
next if request.nil?
|
140
248
|
|
249
|
+
timed_level = request[:timed_level]
|
250
|
+
req_detailed = SqDetailedMetrics::Request.new if timed_level >= 2
|
251
|
+
|
252
|
+
# shrinkwrap_timer = request[:timed_shrinkwrap]
|
253
|
+
# shrinkwrap_timer.start
|
254
|
+
|
141
255
|
Thread.current[:sqreen_http_request] = nil
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
# => BinnedMetrics
|
158
|
-
metric_name = "sq.#{rule}.#{whence}"
|
159
|
-
unless metrics_engine.metric?(metric_name)
|
160
|
-
metrics_engine.create_metric(
|
161
|
-
'name' => metric_name,
|
162
|
-
'period' => 60,
|
163
|
-
'kind' => 'Binning',
|
164
|
-
'options' => { 'base' => 2.0, 'factor' => 0.1 },
|
165
|
-
)
|
256
|
+
request_timer = request[:request_timer]
|
257
|
+
now = request_timer.stop
|
258
|
+
|
259
|
+
if timed_level >= 1
|
260
|
+
request[:timed_callbacks].each do |timer|
|
261
|
+
duration_ms = timer.duration * 1000.0
|
262
|
+
# XXX: the timer tag should have this structured data;
|
263
|
+
# it would be better than recomputing this for every measurement
|
264
|
+
metric_name = ::Sqreen::Weave::Legacy::Instrumentation.tag_to_metric_name(timer.tag)
|
265
|
+
|
266
|
+
next unless metric_name
|
267
|
+
|
268
|
+
metrics_engine.update(metric_name, now, nil, duration_ms)
|
269
|
+
duration_ms *= -1.0 if timer.conditions_passed
|
270
|
+
req_detailed.add_measurement metric_name, duration_ms if req_detailed
|
166
271
|
end
|
167
|
-
metrics_engine.update(metric_name, now, nil, duration * 1000)
|
168
272
|
end
|
169
273
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
'period' => 60,
|
188
|
-
'kind' => 'Binning',
|
189
|
-
'options' => { 'base' => 2.0, 'factor' => 0.1 },
|
190
|
-
)
|
191
|
-
end
|
192
|
-
metrics_engine.update(metric_name, now, nil, duration * 1000)
|
193
|
-
|
194
|
-
metric_name = 'sq.hooks_failing.failing'
|
195
|
-
duration = request[:timed_hooks_raised].sum(&:duration)
|
196
|
-
unless metrics_engine.metric?(metric_name)
|
197
|
-
metrics_engine.create_metric(
|
198
|
-
'name' => metric_name,
|
199
|
-
'period' => 60,
|
200
|
-
'kind' => 'Binning',
|
201
|
-
'options' => { 'base' => 2.0, 'factor' => 0.1 },
|
202
|
-
)
|
274
|
+
sqreen_timer = request[:sqreen_timer]
|
275
|
+
Sqreen::Weave.logger.debug do
|
276
|
+
"request sqreen_timer.total: #{'%.03fus' % (sqreen_timer.duration * 1_000_000)}"
|
277
|
+
end if Sqreen::Weave.logger.debug?
|
278
|
+
Sqreen::Weave.logger.debug do
|
279
|
+
"request request_timer.total: #{'%.03fus' % (request_timer.duration * 1_000_000)}"
|
280
|
+
end if Sqreen::Weave.logger.debug?
|
281
|
+
|
282
|
+
if timed_level >= 1 && Sqreen::Weave.logger.debug?
|
283
|
+
skipped = request[:skipped_callbacks].map(&:name)
|
284
|
+
Sqreen::Weave.logger.debug { "request callback.skipped.count: #{skipped.count}" } if Sqreen::Weave.logger.debug?
|
285
|
+
timings = request[:timed_callbacks].map(&:to_s)
|
286
|
+
total = request[:timed_callbacks].sum(&:duration)
|
287
|
+
Sqreen::Weave.logger.debug { "request callback.total: #{'%.03fus' % (total * 1_000_000)} callback.count: #{timings.count}" } if Sqreen::Weave.logger.debug?
|
288
|
+
timings = request[:timed_hooks].map(&:to_s)
|
289
|
+
total = request[:timed_hooks].sum(&:duration)
|
290
|
+
Sqreen::Weave.logger.debug { "request hook.total: #{'%.03fus' % (total * 1_000_000)} hook.count: #{timings.count}" } if Sqreen::Weave.logger.debug?
|
203
291
|
end
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
timings = request[:timed_hooks].map(&:to_s)
|
215
|
-
total = request[:timed_hooks].sum(&:duration)
|
216
|
-
Sqreen::Weave.logger.debug { "request:#{request[:uuid]} hook.total: #{'%.03fus' % (total * 1_000_000)} hook.timings: [#{timings.join(', ')}]" }
|
217
|
-
|
218
|
-
skipped = request[:skipped_callbacks].map(&:name)
|
219
|
-
skipped_rule_name = skipped.first && skipped.first =~ /weave,rule=(.*)$/ && $1
|
220
|
-
Sqreen.observations_queue.push(['request_overtime', skipped_rule_name, 1, utc_now]) if skipped_rule_name
|
221
|
-
|
222
|
-
sqreen_request_duration = total
|
223
|
-
Sqreen.observations_queue.push(['sq', nil, sqreen_request_duration * 1000, utc_now])
|
224
|
-
|
225
|
-
request_duration = now - request[:start_time]
|
226
|
-
Sqreen.observations_queue.push(['req', nil, request_duration * 1000, utc_now])
|
292
|
+
|
293
|
+
overtime_cb = ::Sqreen::Weave::Legacy::Instrumentation.tag_to_metric_name(request[:overtime_cb]) \
|
294
|
+
if request[:overtime_cb]
|
295
|
+
metrics_engine.update('request_overtime', now, overtime_cb, 1) if overtime_cb
|
296
|
+
|
297
|
+
sqreen_request_duration = sqreen_timer.duration * 1000.0
|
298
|
+
metrics_engine.update('sq', now, nil, sqreen_request_duration)
|
299
|
+
|
300
|
+
request_duration = request_timer.duration * 1000.0
|
301
|
+
metrics_engine.update('req', now, nil, request_duration)
|
227
302
|
|
228
303
|
sqreen_request_ratio = (sqreen_request_duration * 100.0) / (request_duration - sqreen_request_duration)
|
229
|
-
|
304
|
+
metrics_engine.update('pct', now, nil, sqreen_request_ratio)
|
305
|
+
Sqreen::Weave.logger.debug { "request sqreen_timer.ratio: #{'%.03f' % (sqreen_request_ratio / 100.0)}" } if Sqreen::Weave.logger.debug?
|
306
|
+
|
307
|
+
if req_detailed
|
308
|
+
req_detailed.route = request[:route]
|
309
|
+
req_detailed.overtime_cb = overtime_cb if overtime_cb
|
310
|
+
req_detailed.add_measurement 'sq', sqreen_request_duration
|
311
|
+
req_detailed.add_measurement 'req', request_duration
|
312
|
+
|
313
|
+
metrics_engine.update(REQ_LVL_2_METRIC, now, nil, req_detailed)
|
314
|
+
end
|
315
|
+
|
316
|
+
# shrinkwrap_timer.stop
|
317
|
+
|
318
|
+
# duration = shrinkwrap_timer.duration
|
319
|
+
# metrics_engine.update('sq.shrinkwrap', now, nil, duration * 1000)
|
230
320
|
end
|
231
321
|
end.install
|
232
322
|
|
233
323
|
### globally declare instrumentation ready
|
234
324
|
Sqreen.instrumentation_ready = true
|
325
|
+
Sqreen::Weave.logger.info { "Instrumentation activated" }
|
235
326
|
end
|
236
327
|
|
237
328
|
# needed by Sqreen::Runner
|
238
329
|
def remove_all_callbacks
|
239
330
|
Sqreen.instrumentation_ready = false
|
331
|
+
Sqreen::Weave.logger.info { "Instrumentation deactivated" }
|
240
332
|
|
241
333
|
loop do
|
242
334
|
hook = @hooks.pop
|
@@ -253,6 +345,15 @@ class Sqreen::Weave::Legacy::Instrumentation
|
|
253
345
|
klass = callback.klass
|
254
346
|
method = callback.method
|
255
347
|
|
348
|
+
if (call_count = ENV['SQREEN_DEBUG_CALL_COUNT'])
|
349
|
+
call_count = JSON.parse(call_count)
|
350
|
+
if callback.respond_to?(:rule_name) && call_count.key?(callback.rule_name)
|
351
|
+
count = call_count[callback.rule_name]
|
352
|
+
Sqreen::Weave.logger.debug { "override rule: #{callback.rule_name} call_count: #{count.inspect}" }
|
353
|
+
callback.instance_eval { @call_count_interval = call_count[callback.rule_name] }
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
256
357
|
if Sqreen::Graft::HookPoint.new("#{klass}.#{method}").exist?
|
257
358
|
hook_point = "#{klass}.#{method}"
|
258
359
|
elsif Sqreen::Graft::HookPoint.new("#{klass}##{method}").exist?
|
@@ -268,14 +369,14 @@ class Sqreen::Weave::Legacy::Instrumentation
|
|
268
369
|
hook = Sqreen::Graft::Hook[hook_point, strategy]
|
269
370
|
hook.add do
|
270
371
|
if callback.pre?
|
271
|
-
|
372
|
+
use_flow = block || callback.is_a?(::Sqreen::Conditionable)
|
373
|
+
before(rule, rank: priority, mandatory: !callback.overtimeable, flow: use_flow, ignore: ignore) do |call, b|
|
272
374
|
next unless Thread.current[:sqreen_http_request]
|
273
375
|
|
274
376
|
i = call.instance
|
275
377
|
a = call.args
|
276
378
|
r = call.remaining
|
277
379
|
|
278
|
-
Sqreen::Weave.logger.debug { "#{rule} klass=#{callback.klass} method=#{callback.method} when=#pre instance=#{i}" }
|
279
380
|
begin
|
280
381
|
ret = callback.pre(i, a, r)
|
281
382
|
rescue StandardError => e
|
@@ -286,17 +387,30 @@ class Sqreen::Weave::Legacy::Instrumentation
|
|
286
387
|
Sqreen::RemoteException.record(e)
|
287
388
|
end
|
288
389
|
end
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
390
|
+
|
391
|
+
next if ret.nil? || !ret.is_a?(Hash)
|
392
|
+
|
393
|
+
throw_val =
|
394
|
+
case ret[:status]
|
395
|
+
when :skip, 'skip'
|
396
|
+
b.return(ret[:new_return_value]).break! if ret.key?(:new_return_value)
|
397
|
+
when :modify_args, 'modify_args'
|
398
|
+
b.args(ret[:args])
|
399
|
+
when :raise, 'raise'
|
400
|
+
if ret.key?(:exception)
|
401
|
+
b.raise(ret[:exception])
|
402
|
+
else
|
403
|
+
b.raise(Sqreen::AttackBlocked.new("Sqreen blocked a security threat (type: #{callback.rule_name}). No action is required."))
|
404
|
+
end
|
405
|
+
end if block
|
406
|
+
|
407
|
+
if ret && ret[:passed_conditions]
|
408
|
+
throw_val ||= b.noop
|
409
|
+
throw_val.passed_conditions!
|
410
|
+
end
|
411
|
+
next unless throw_val
|
412
|
+
throw_val.break! if ret[:skip_rem_cbs]
|
413
|
+
throw(b, throw_val)
|
300
414
|
end
|
301
415
|
end
|
302
416
|
|
@@ -309,7 +423,6 @@ class Sqreen::Weave::Legacy::Instrumentation
|
|
309
423
|
a = call.args
|
310
424
|
r = call.remaining
|
311
425
|
|
312
|
-
Sqreen::Weave.logger.debug { "#{rule} klass=#{callback.klass} method=#{callback.method} when=#post instance=#{i}" }
|
313
426
|
begin
|
314
427
|
ret = callback.post(v, i, a, r)
|
315
428
|
rescue StandardError => e
|
@@ -320,15 +433,22 @@ class Sqreen::Weave::Legacy::Instrumentation
|
|
320
433
|
Sqreen::RemoteException.record(e)
|
321
434
|
end
|
322
435
|
end
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
436
|
+
|
437
|
+
throw_val =
|
438
|
+
case ret[:status]
|
439
|
+
when :override, 'override'
|
440
|
+
b.return(ret[:new_return_value]) if ret.key?(:new_return_value)
|
441
|
+
when :raise, 'raise'
|
442
|
+
b.raise(ret[:exception]) if ret.key?(:exception)
|
443
|
+
b.raise(Sqreen::AttackBlocked.new("Sqreen blocked a security threat (type: #{callback.rule_name}). No action is required."))
|
444
|
+
end unless ret.nil? || !ret.is_a?(Hash) || !block
|
445
|
+
|
446
|
+
if ret && ret[:passed_conditions]
|
447
|
+
throw_val ||= b.noop
|
448
|
+
throw_val.passed_conditions!
|
449
|
+
end
|
450
|
+
next unless throw_val
|
451
|
+
throw(b, throw_val)
|
332
452
|
end
|
333
453
|
end
|
334
454
|
|
@@ -341,7 +461,6 @@ class Sqreen::Weave::Legacy::Instrumentation
|
|
341
461
|
a = call.args
|
342
462
|
r = call.remaining
|
343
463
|
|
344
|
-
Sqreen::Weave.logger.debug { "#{rule} klass=#{callback.klass} method=#{callback.method} when=#failing instance=#{i}" }
|
345
464
|
begin
|
346
465
|
ret = callback.failing(e, i, a, r)
|
347
466
|
rescue StandardError => e
|
@@ -352,23 +471,30 @@ class Sqreen::Weave::Legacy::Instrumentation
|
|
352
471
|
Sqreen::RemoteException.record(e)
|
353
472
|
end
|
354
473
|
end
|
355
|
-
Sqreen::Weave.logger.debug { "#{rule} klass=#{callback.klass} method=#{callback.method} when=#failing instance=#{i} => return=#{ret.inspect}" }
|
356
474
|
|
357
475
|
throw(b, b.raise(e)) if ret.nil? || !ret.is_a?(Hash)
|
358
476
|
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
477
|
+
throw_val =
|
478
|
+
case ret[:status]
|
479
|
+
when :override, 'override'
|
480
|
+
b.return(ret[:new_return_value]) if ret.key?(:new_return_value)
|
481
|
+
when :retry, 'retry'
|
482
|
+
b.retry
|
483
|
+
when :raise, 'raise'
|
484
|
+
b.raise(ret[:exception]) if ret.key?(:exception)
|
485
|
+
b.raise(Sqreen::AttackBlocked.new("Sqreen blocked a security threat (type: #{callback.rule_name}). No action is required."))
|
486
|
+
when :reraise, 'reraise'
|
487
|
+
b.raise(e)
|
488
|
+
else
|
489
|
+
b.raise(e)
|
490
|
+
end unless ret.nil? || !ret.is_a?(Hash) || !block
|
491
|
+
|
492
|
+
if ret && ret[:passed_conditions]
|
493
|
+
throw_val ||= b.noop
|
494
|
+
throw_val.passed_conditions!
|
495
|
+
end
|
496
|
+
next unless throw_val
|
497
|
+
throw(b, throw_val)
|
372
498
|
end
|
373
499
|
end
|
374
500
|
end.install
|
@@ -403,4 +529,20 @@ class Sqreen::Weave::Legacy::Instrumentation
|
|
403
529
|
Sqreen::Rules::RunUserActions.new(Sqreen, :auth_track, 1),
|
404
530
|
]
|
405
531
|
end
|
532
|
+
|
533
|
+
def self.tag_to_metric_name(tag)
|
534
|
+
cached = @cache_tag_to_metric[tag]
|
535
|
+
return cached unless cached.nil?
|
536
|
+
|
537
|
+
tag =~ /weave,rule=(.*)$/ && rule = $1 and # rubocop:disable Style/AndOr
|
538
|
+
(tag =~ /@before/ && whence = 'pre' or # rubocop:disable Style/AndOr
|
539
|
+
tag =~ /@after/ && whence = 'post' or # rubocop:disable Style/AndOr
|
540
|
+
tag =~ /@raised/ && whence = 'failing' or # rubocop:disable Style/AndOr
|
541
|
+
tag =~ /@ensured/ && whence = 'finally')
|
542
|
+
|
543
|
+
@cache_tag_to_metric[tag] =
|
544
|
+
rule && whence ? "sq.#{rule}.#{whence}" : false
|
545
|
+
end
|
546
|
+
|
547
|
+
@cache_tag_to_metric = {}
|
406
548
|
end
|