sqreen 1.12.0 → 1.13.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/sqreen/actions.rb +68 -10
- data/lib/sqreen/configuration.rb +2 -0
- data/lib/sqreen/events/request_record.rb +40 -3
- data/lib/sqreen/frameworks/generic.rb +18 -4
- data/lib/sqreen/instrumentation.rb +22 -2
- data/lib/sqreen/metrics.rb +1 -0
- data/lib/sqreen/metrics/binning.rb +74 -0
- data/lib/sqreen/metrics_store.rb +6 -2
- data/lib/sqreen/performance_notifications/binned_metrics.rb +119 -0
- data/lib/sqreen/rules_callbacks/run_block_user_actions.rb +34 -0
- data/lib/sqreen/runner.rb +23 -0
- data/lib/sqreen/sdk.rb +2 -2
- data/lib/sqreen/shared_storage.rb +5 -0
- data/lib/sqreen/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fb4ef79ad2d573f02250c303800c9d4c8b64a3f1e0318fbf2f7e198508826b7b
|
4
|
+
data.tar.gz: 991f85b77cb27f9387fb027e74538aded1a7f0e4dd0c1a78a8a89041154d790a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a575ae38cbccc9381e8dea3fedc7c7ac24ce0a7174b0ba799f264cfbd8bf0a346c4c60fc6518172f6bff09d692193d469cb31fe4513781f67468d52c7db14419
|
7
|
+
data.tar.gz: 8dffc28661b0e8fd608cae0917326519abf7c6d8f4b36d96ffead33e6df3059f0de05187a03d425d21068e36a04f8b6f0b42c9375bce9a9a7515c1fe6f847ff4
|
data/lib/sqreen/actions.rb
CHANGED
@@ -6,6 +6,7 @@ require 'sqreen/log'
|
|
6
6
|
require 'sqreen/exception'
|
7
7
|
require 'sqreen/sdk'
|
8
8
|
require 'sqreen/frameworks'
|
9
|
+
require 'singleton'
|
9
10
|
|
10
11
|
module Sqreen
|
11
12
|
# Implements actions (behavior taken in response to agent signals)
|
@@ -58,25 +59,39 @@ module Sqreen
|
|
58
59
|
return nil
|
59
60
|
end
|
60
61
|
|
61
|
-
|
62
|
+
opts = {
|
63
|
+
:duration => duration,
|
64
|
+
:send_response => hash['send_response'],
|
65
|
+
}
|
66
|
+
|
67
|
+
subclass.new(id, opts, hash['parameters'] || {})
|
62
68
|
end
|
63
69
|
|
70
|
+
# Base class for actions
|
64
71
|
class Base
|
65
|
-
attr_reader :id, :expiry
|
72
|
+
attr_reader :id, :expiry, :send_response
|
66
73
|
|
67
|
-
def initialize(id,
|
74
|
+
def initialize(id, opts)
|
68
75
|
@id = id
|
76
|
+
duration = opts[:duration]
|
69
77
|
@expiry = Time.new + duration unless duration.nil?
|
78
|
+
@send_response = if opts[:send_response].nil?
|
79
|
+
true
|
80
|
+
else
|
81
|
+
!!opts[:send_response]
|
82
|
+
end
|
70
83
|
end
|
71
84
|
|
72
85
|
# See Sqreen::CB for return values
|
73
86
|
def run(*args)
|
74
87
|
return if expiry && Time.new > expiry
|
75
88
|
ret = do_run *args
|
76
|
-
unless ret.nil?
|
89
|
+
unless ret.nil? || !@send_response
|
77
90
|
Sqreen.internal_track(event_name,
|
78
|
-
'properties' =>
|
79
|
-
|
91
|
+
'properties' => {
|
92
|
+
'output' => event_properties(*args),
|
93
|
+
'action_id' => id,
|
94
|
+
})
|
80
95
|
end
|
81
96
|
ret
|
82
97
|
end
|
@@ -155,8 +170,8 @@ module Sqreen
|
|
155
170
|
include IpRanges
|
156
171
|
self.type_name = 'block_ip'
|
157
172
|
|
158
|
-
def initialize(id,
|
159
|
-
super(id,
|
173
|
+
def initialize(id, opts, params = {})
|
174
|
+
super(id, opts)
|
160
175
|
parse_ip_ranges params
|
161
176
|
end
|
162
177
|
|
@@ -179,8 +194,8 @@ module Sqreen
|
|
179
194
|
|
180
195
|
attr_reader :redirect_url
|
181
196
|
|
182
|
-
def initialize(id,
|
183
|
-
super(id,
|
197
|
+
def initialize(id, opts, params = {})
|
198
|
+
super(id, opts)
|
184
199
|
@redirect_url = params['url']
|
185
200
|
raise "no url provided for action #{id}" unless @redirect_url
|
186
201
|
parse_ip_ranges params
|
@@ -199,5 +214,48 @@ module Sqreen
|
|
199
214
|
{ 'ip_address' => client_ip, 'url' => @redirect_url }
|
200
215
|
end
|
201
216
|
end
|
217
|
+
|
218
|
+
# Blocks a user at the point Sqreen::identify()
|
219
|
+
# or Sqreen::auth_track() are called
|
220
|
+
class BlockUser < Base
|
221
|
+
self.type_name = 'block_user'
|
222
|
+
|
223
|
+
def initialize(id, opts, params = {})
|
224
|
+
super(id, opts)
|
225
|
+
@users = params['users']
|
226
|
+
raise ::Sqreen::Exception, 'nil "users" param for block_user action' if @users.nil?
|
227
|
+
raise ::Sqreen::Exception, '"users" param must be an array' unless @users.is_a? Array
|
228
|
+
end
|
229
|
+
|
230
|
+
def do_run(identity_params)
|
231
|
+
return unless @users.include? stringify_keys(identity_params)
|
232
|
+
Sqreen.log.info(
|
233
|
+
"Will raise due to user being blocked by action #{id}. " \
|
234
|
+
"Blocked user identity: #{identity_params}"
|
235
|
+
)
|
236
|
+
|
237
|
+
e = Sqreen::AttackBlocked.new(
|
238
|
+
"Blocked user with identity #{identity_params} " \
|
239
|
+
'due to automatic security response. No action is required'
|
240
|
+
)
|
241
|
+
|
242
|
+
{
|
243
|
+
:status => :raise,
|
244
|
+
:exception => e,
|
245
|
+
}
|
246
|
+
end
|
247
|
+
|
248
|
+
def event_properties(identity_params)
|
249
|
+
{ 'user' => identity_params }
|
250
|
+
end
|
251
|
+
|
252
|
+
private
|
253
|
+
|
254
|
+
def stringify_keys(hash)
|
255
|
+
Hash[
|
256
|
+
hash.map { |k, v| [k.to_s, v] }
|
257
|
+
]
|
258
|
+
end
|
259
|
+
end
|
202
260
|
end
|
203
261
|
end
|
data/lib/sqreen/configuration.rb
CHANGED
@@ -42,9 +42,7 @@ module Sqreen
|
|
42
42
|
end
|
43
43
|
end
|
44
44
|
if observed[:sdk]
|
45
|
-
res[:observed][:sdk] =
|
46
|
-
{ :name => meth, :time => time, :args => args }
|
47
|
-
end
|
45
|
+
res[:observed][:sdk] = processed_sdk_calls
|
48
46
|
end
|
49
47
|
end
|
50
48
|
res[:local] = payload['local'] if payload['local']
|
@@ -58,5 +56,44 @@ module Sqreen
|
|
58
56
|
res[:request][:headers] = payload['headers'] if payload['headers']
|
59
57
|
res
|
60
58
|
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def processed_sdk_calls
|
63
|
+
auth_keys = last_identify_id
|
64
|
+
|
65
|
+
observed[:sdk].map do |meth, time, *args|
|
66
|
+
{
|
67
|
+
:name => meth,
|
68
|
+
:time => time,
|
69
|
+
:args => inject_identifiers(args, meth, auth_keys),
|
70
|
+
}
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def inject_identifiers(args, meth, auth_keys)
|
75
|
+
return args unless meth == :track && auth_keys
|
76
|
+
|
77
|
+
track_opts = args[1] || {}
|
78
|
+
if track_opts[:user_identifiers].nil?
|
79
|
+
args[1] = track_opts.dup
|
80
|
+
args[1][:user_identifiers] = auth_keys
|
81
|
+
elsif track_opts[:user_identifiers] != auth_keys
|
82
|
+
Sqreen.log.warn 'Sqreen.identify and Sqreen.track have been called ' \
|
83
|
+
'with different user_identifiers values'
|
84
|
+
end
|
85
|
+
|
86
|
+
args
|
87
|
+
end
|
88
|
+
|
89
|
+
def last_identify_id
|
90
|
+
return nil unless observed[:sdk]
|
91
|
+
|
92
|
+
observed[:sdk].reverse_each do |meth, _time, *args|
|
93
|
+
next unless meth == :identify
|
94
|
+
return args.first if args.respond_to? :first
|
95
|
+
end
|
96
|
+
nil
|
97
|
+
end
|
61
98
|
end
|
62
99
|
end
|
@@ -44,16 +44,30 @@ module Sqreen
|
|
44
44
|
ENV['RACK_ENV'] == 'development'
|
45
45
|
end
|
46
46
|
|
47
|
-
|
47
|
+
PREFERRED_IP_HEADERS = %w[HTTP_X_FORWARDED_FOR HTTP_X_REAL_IP
|
48
48
|
HTTP_CLIENT_IP HTTP_X_FORWARDED
|
49
49
|
HTTP_X_CLUSTER_CLIENT_IP HTTP_FORWARDED_FOR
|
50
|
-
HTTP_FORWARDED HTTP_VIA
|
50
|
+
HTTP_FORWARDED HTTP_VIA].freeze
|
51
|
+
|
52
|
+
def preferred_ip_headers
|
53
|
+
@preferred_ip_headers ||=
|
54
|
+
begin
|
55
|
+
header_name = Sqreen.config_get(:ip_header)
|
56
|
+
if header_name
|
57
|
+
env_var = 'HTTP_' + header_name.tr('-', '_').upcase
|
58
|
+
[env_var] + (PREFERRED_IP_HEADERS - [env_var])
|
59
|
+
else
|
60
|
+
PREFERRED_IP_HEADERS
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
private :preferred_ip_headers
|
51
65
|
|
52
66
|
def ip_headers
|
53
67
|
req = request
|
54
68
|
return [] unless req
|
55
69
|
ips = []
|
56
|
-
(
|
70
|
+
(preferred_ip_headers + ['REMOTE_ADDR']).each do |header|
|
57
71
|
v = req.env[header]
|
58
72
|
ips << [header, v] unless v.nil?
|
59
73
|
end
|
@@ -79,7 +93,7 @@ module Sqreen
|
|
79
93
|
return nil unless req
|
80
94
|
# Look for an external address being forwarded
|
81
95
|
split_ips = []
|
82
|
-
|
96
|
+
preferred_ip_headers.each do |header_name|
|
83
97
|
forwarded = req.env[header_name]
|
84
98
|
ips = split_ip_addresses(forwarded)
|
85
99
|
lip = ips.find { |ip| (ip !~ TRUSTED_PROXIES) && valid_ip?(ip) }
|
@@ -11,6 +11,7 @@ require 'sqreen/rules_signature'
|
|
11
11
|
require 'sqreen/shared_storage'
|
12
12
|
require 'sqreen/rules_callbacks/record_request_context'
|
13
13
|
require 'sqreen/rules_callbacks/run_req_start_actions'
|
14
|
+
require 'sqreen/rules_callbacks/run_block_user_actions'
|
14
15
|
require 'set'
|
15
16
|
|
16
17
|
# How to override a class method:
|
@@ -245,6 +246,10 @@ module Sqreen
|
|
245
246
|
def self.define_callback_method(meth, original_meth, klass_name)
|
246
247
|
@sqreen_multi_instr ||= nil
|
247
248
|
proc do |*args, &block|
|
249
|
+
record_req_hp = @@record_request_hookpoints.include?([klass_name, meth]) &&
|
250
|
+
Sqreen::PerformanceNotifications.listen_for?
|
251
|
+
Sqreen::PerformanceNotifications::BinnedMetrics.start_request if record_req_hp
|
252
|
+
|
248
253
|
budget = nil
|
249
254
|
skip_call = Thread.current[:sqreen_in_use]
|
250
255
|
begin
|
@@ -370,6 +375,7 @@ module Sqreen
|
|
370
375
|
next unless ret.is_a? Hash
|
371
376
|
case ret[:status]
|
372
377
|
when :raise, 'raise'
|
378
|
+
raise ret[:exception] if ret.key?(:exception)
|
373
379
|
raise Sqreen::AttackBlocked, "Sqreen blocked a security threat (type: #{ret[:rule_name]}). No action is required."
|
374
380
|
when :override, 'override'
|
375
381
|
result = ret[:new_return_value]
|
@@ -379,10 +385,11 @@ module Sqreen
|
|
379
385
|
end
|
380
386
|
result
|
381
387
|
ensure
|
382
|
-
if
|
388
|
+
if record_req_hp
|
383
389
|
Sqreen::PerformanceNotifications.instrument('Callbacks/hooks_reporting/pre') do
|
384
390
|
Sqreen::PerformanceNotifications::LogPerformance.next_request
|
385
391
|
Sqreen::PerformanceNotifications::NewRelic.next_request
|
392
|
+
Sqreen::PerformanceNotifications::BinnedMetrics.finish_request
|
386
393
|
end
|
387
394
|
end
|
388
395
|
Thread.current[:sqreen_in_use] = false
|
@@ -439,6 +446,8 @@ module Sqreen
|
|
439
446
|
end
|
440
447
|
alias_method meth, saved_meth_name
|
441
448
|
send(method_kind, meth)
|
449
|
+
|
450
|
+
remove_method saved_meth_name
|
442
451
|
end
|
443
452
|
end
|
444
453
|
|
@@ -450,6 +459,15 @@ module Sqreen
|
|
450
459
|
saved_meth_name = get_saved_method_name(meth)
|
451
460
|
new_method = "#{meth}_modified".to_sym
|
452
461
|
|
462
|
+
# do not include ancestors o/wise we might get a saved method from a
|
463
|
+
# superclass which is never called because the original name
|
464
|
+
# (instrumented) method of the superclass has been overridden
|
465
|
+
private_meths = klass_name.private_instance_methods(false)
|
466
|
+
if private_meths.include?(saved_meth_name)
|
467
|
+
Sqreen.log.debug { "#{saved_meth_name} found #{klass_name}##{meth} already instrumented" }
|
468
|
+
return saved_meth_name
|
469
|
+
end
|
470
|
+
|
453
471
|
p = Instrumentation.define_callback_method(meth, saved_meth_name,
|
454
472
|
klass_name)
|
455
473
|
method_kind = nil
|
@@ -664,7 +682,9 @@ module Sqreen
|
|
664
682
|
# @return [Array<Sqreen::CB>]
|
665
683
|
def hardcoded_callbacks(framework)
|
666
684
|
[
|
667
|
-
Sqreen::Rules::RunReqStartActions.new(framework)
|
685
|
+
Sqreen::Rules::RunReqStartActions.new(framework),
|
686
|
+
Sqreen::Rules::RunBlockUserActions.new(Sqreen, :identify, 0),
|
687
|
+
Sqreen::Rules::RunBlockUserActions.new(Sqreen, :auth_track, 1),
|
668
688
|
]
|
669
689
|
end
|
670
690
|
|
data/lib/sqreen/metrics.rb
CHANGED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Copyright (c) 2018 Sqreen. All Rights Reserved.
|
2
|
+
# Please refer to our terms for more information: https://www.sqreen.io/terms.html
|
3
|
+
|
4
|
+
require 'sqreen/metrics/base'
|
5
|
+
|
6
|
+
module Sqreen
|
7
|
+
module Metric
|
8
|
+
# Takes numbers as samples and bins them (effectively, rounds them).
|
9
|
+
class Binning < Base
|
10
|
+
# upper bound of i-th bin is factor * base^(i-1)
|
11
|
+
def initialize(base, factor)
|
12
|
+
super()
|
13
|
+
@base = base
|
14
|
+
@factor = factor
|
15
|
+
log_base = Math.log(base)
|
16
|
+
log_factor = Math.log(factor)
|
17
|
+
@inv_log_base = 1 / log_base
|
18
|
+
@add_parcel = - log_factor / log_base
|
19
|
+
new_sample(Time.now.utc)
|
20
|
+
end
|
21
|
+
|
22
|
+
def update(_at, _key, x)
|
23
|
+
h = @sample[OBSERVATION_KEY]
|
24
|
+
bin = bin_no(x)
|
25
|
+
h[bin] += 1
|
26
|
+
end
|
27
|
+
|
28
|
+
def next_sample(time)
|
29
|
+
return nil if @sample[OBSERVATION_KEY].empty?
|
30
|
+
super(time)
|
31
|
+
end
|
32
|
+
|
33
|
+
protected
|
34
|
+
|
35
|
+
# these two are called by next_sample
|
36
|
+
|
37
|
+
def finalize_sample(time)
|
38
|
+
@sample[FINISH_KEY] = time
|
39
|
+
h = @sample[OBSERVATION_KEY]
|
40
|
+
@sample[OBSERVATION_KEY] = { 'u' => @factor, 'b' => @base, 'v' => h }
|
41
|
+
end
|
42
|
+
|
43
|
+
def new_sample(time)
|
44
|
+
@sample = {
|
45
|
+
OBSERVATION_KEY => Hash.new { |hash, key| hash[key] = 0 },
|
46
|
+
START_KEY => time,
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def bin_no(x)
|
53
|
+
res = 2 + (@inv_log_base * Math.log(x) + @add_parcel).floor
|
54
|
+
res < 1 ? 1 : res
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# overrides the constructor
|
59
|
+
class ParameterizedBinning < Binning
|
60
|
+
def initialize
|
61
|
+
raise 'Not configured' unless self.class.base && self.class.factor
|
62
|
+
super(self.class.base, self.class.factor)
|
63
|
+
end
|
64
|
+
|
65
|
+
class << self
|
66
|
+
attr_reader :base, :factor
|
67
|
+
def parameters(opts)
|
68
|
+
@base = opts[:base]
|
69
|
+
@factor = opts[:factor]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
data/lib/sqreen/metrics_store.rb
CHANGED
@@ -33,7 +33,7 @@ module Sqreen
|
|
33
33
|
|
34
34
|
# Definition contains a name,period and aggregate at least
|
35
35
|
# @param definition [Hash] a metric definition
|
36
|
-
# @param
|
36
|
+
# @param mklass [Object] Override metric object (used in testing)
|
37
37
|
def create_metric(definition, mklass = nil)
|
38
38
|
name = definition[NAME_KEY]
|
39
39
|
kind = definition[KIND_KEY]
|
@@ -47,6 +47,10 @@ module Sqreen
|
|
47
47
|
metric
|
48
48
|
end
|
49
49
|
|
50
|
+
def metric?(name)
|
51
|
+
@metrics.key?(name)
|
52
|
+
end
|
53
|
+
|
50
54
|
def update(name, at, key, value)
|
51
55
|
at = at.utc
|
52
56
|
metric, period, start = @metrics[name]
|
@@ -71,7 +75,7 @@ module Sqreen
|
|
71
75
|
def next_sample(name, at)
|
72
76
|
metric = @metrics[name][0]
|
73
77
|
r = metric.next_sample(at)
|
74
|
-
@metrics[name][2] = at
|
78
|
+
@metrics[name][2] = at # start
|
75
79
|
if r
|
76
80
|
r[NAME_KEY] = name
|
77
81
|
obs = r[Metric::OBSERVATION_KEY]
|
@@ -0,0 +1,119 @@
|
|
1
|
+
# Copyright (c) 2018 Sqreen. All Rights Reserved.
|
2
|
+
# Please refer to our terms for more information: https://www.sqreen.io/terms.html
|
3
|
+
|
4
|
+
require 'sqreen/performance_notifications'
|
5
|
+
|
6
|
+
module Sqreen
|
7
|
+
#
|
8
|
+
module PerformanceNotifications
|
9
|
+
# Logs callback performance
|
10
|
+
class BinnedMetrics
|
11
|
+
EVENT_REQ = 'req'.freeze # request total time
|
12
|
+
EVENT_TOTAL_TIME = 'sq'.freeze # sqreen total overhead callback time
|
13
|
+
|
14
|
+
EVT_NAME_REGEXP = %r{\ACallbacks/([^/]+)/([^/]+)\z}
|
15
|
+
|
16
|
+
# @param metrics_store [Sqreen::MetricsStore]
|
17
|
+
def initialize(metrics_store, period)
|
18
|
+
@metrics_store = metrics_store
|
19
|
+
@period = period
|
20
|
+
@subid = nil
|
21
|
+
end
|
22
|
+
|
23
|
+
def enable
|
24
|
+
return unless @subid.nil?
|
25
|
+
|
26
|
+
metrics_store.create_metric(
|
27
|
+
'name' => EVENT_REQ, 'period' => period, 'kind' => 'ParameterizedBinning'
|
28
|
+
)
|
29
|
+
metrics_store.create_metric(
|
30
|
+
'name' => EVENT_TOTAL_TIME, 'period' => period, 'kind' => 'ParameterizedBinning'
|
31
|
+
)
|
32
|
+
|
33
|
+
@subid = Sqreen::PerformanceNotifications.subscribe(&method(:log))
|
34
|
+
end
|
35
|
+
|
36
|
+
def disable
|
37
|
+
return if @subid.nil?
|
38
|
+
Sqreen::PerformanceNotifications.unsubscribe(@subid)
|
39
|
+
@subid = nil
|
40
|
+
end
|
41
|
+
|
42
|
+
def log(event, start, finish, _meta)
|
43
|
+
return unless event =~ EVT_NAME_REGEXP
|
44
|
+
rule, cb = Regexp.last_match.captures
|
45
|
+
|
46
|
+
metric_name = "sq.#{rule}.#{cb}"
|
47
|
+
ensure_metric(metric_name)
|
48
|
+
|
49
|
+
finish_time = SQREEN_MONO_TIME ? Time.now.utc : finish
|
50
|
+
time_millis = (finish - start) * 1000
|
51
|
+
SharedStorage[:sqreen_request_time] += time_millis
|
52
|
+
metrics_store.update(metric_name, finish_time, nil, time_millis)
|
53
|
+
end
|
54
|
+
|
55
|
+
def start_request
|
56
|
+
SharedStorage[:request_start_time] = PerformanceNotifications.time
|
57
|
+
SharedStorage[:sqreen_request_time] = 0.0
|
58
|
+
end
|
59
|
+
|
60
|
+
def finish_request
|
61
|
+
start_time = SharedStorage[:request_start_time]
|
62
|
+
finish_time = PerformanceNotifications.time
|
63
|
+
duration_millis = (finish_time - start_time) * 1000
|
64
|
+
|
65
|
+
finish_time_obj = SQREEN_MONO_TIME ? Time.now.utc : finish_time
|
66
|
+
# format of evt is [cat, key, value, timestamp]
|
67
|
+
Sqreen.observations_queue.push(
|
68
|
+
[EVENT_REQ, nil, duration_millis, finish_time_obj]
|
69
|
+
)
|
70
|
+
Sqreen.observations_queue.push(
|
71
|
+
[EVENT_TOTAL_TIME, nil,
|
72
|
+
SharedStorage[:sqreen_request_time], finish_time_obj]
|
73
|
+
)
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
# @return [Sqreen::MetricsStore]
|
79
|
+
attr_reader :metrics_store
|
80
|
+
attr_reader :period
|
81
|
+
|
82
|
+
def ensure_metric(metric_name)
|
83
|
+
return if metrics_store.metric?(metric_name)
|
84
|
+
metrics_store.create_metric(
|
85
|
+
'name' => metric_name, 'period' => period, 'kind' => 'ParameterizedBinning'
|
86
|
+
)
|
87
|
+
end
|
88
|
+
|
89
|
+
@instance = nil
|
90
|
+
class << self
|
91
|
+
# @return [Sqreen::PerformanceNotifications::BinnedMetrics]
|
92
|
+
attr_reader :instance
|
93
|
+
def enable(metrics_store, period = 60, base = 2.0, factor = 0.1)
|
94
|
+
disable
|
95
|
+
Sqreen::Metric::ParameterizedBinning.parameters(
|
96
|
+
:base => base, :factor => factor
|
97
|
+
)
|
98
|
+
@instance = new(metrics_store, period).tap(&:enable)
|
99
|
+
end
|
100
|
+
|
101
|
+
def disable
|
102
|
+
return unless instance
|
103
|
+
instance.disable
|
104
|
+
@instance = nil
|
105
|
+
end
|
106
|
+
|
107
|
+
def start_request
|
108
|
+
return unless instance
|
109
|
+
instance.start_request
|
110
|
+
end
|
111
|
+
|
112
|
+
def finish_request
|
113
|
+
return unless instance
|
114
|
+
instance.finish_request
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# Copyright (c) 2018 Sqreen. All Rights Reserved.
|
2
|
+
# Please refer to our terms for more information: https://www.sqreen.io/terms.html
|
3
|
+
|
4
|
+
require 'sqreen/rule_callback'
|
5
|
+
require 'sqreen/actions'
|
6
|
+
|
7
|
+
module Sqreen
|
8
|
+
module Rules
|
9
|
+
# Runs the block_user actions (for hooking Sqreen.{identify,auth_user})
|
10
|
+
class RunBlockUserActions < CB
|
11
|
+
def initialize(klass, method, auth_keys_idx)
|
12
|
+
super(klass, method)
|
13
|
+
@auth_keys_idx = auth_keys_idx
|
14
|
+
end
|
15
|
+
|
16
|
+
def post(_retval, _inst, args, _budget = nil)
|
17
|
+
actions = actions_repo[Sqreen::Actions::BlockUser]
|
18
|
+
|
19
|
+
actions.each do |action|
|
20
|
+
res = action.run args[@auth_keys_idx]
|
21
|
+
return res unless res.nil?
|
22
|
+
end
|
23
|
+
nil
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
# @return [Sqreen::Actions::Repository]
|
29
|
+
def actions_repo
|
30
|
+
Sqreen::Actions::Repository.instance
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/sqreen/runner.rb
CHANGED
@@ -17,6 +17,7 @@ require 'sqreen/metrics_store'
|
|
17
17
|
require 'sqreen/deliveries/simple'
|
18
18
|
require 'sqreen/deliveries/batch'
|
19
19
|
require 'sqreen/performance_notifications/metrics'
|
20
|
+
require 'sqreen/performance_notifications/binned_metrics'
|
20
21
|
require 'sqreen/instrumentation'
|
21
22
|
require 'sqreen/call_countable'
|
22
23
|
|
@@ -30,6 +31,11 @@ module Sqreen
|
|
30
31
|
|
31
32
|
METRICS_EVENT = 'metrics'.freeze
|
32
33
|
|
34
|
+
PERF_METRICS_PERIOD = 60 # 1 min
|
35
|
+
DEFAULT_PERF_LEVEL = 0 # disabled
|
36
|
+
DEFAULT_PERF_UNIT = 0.1 # ms
|
37
|
+
DEFAULT_PERF_BASE = 2.0
|
38
|
+
|
33
39
|
class << self
|
34
40
|
attr_reader :features
|
35
41
|
def update_features(features)
|
@@ -204,6 +210,20 @@ module Sqreen
|
|
204
210
|
end
|
205
211
|
end
|
206
212
|
|
213
|
+
def config_binned_metrics(level, base, factor)
|
214
|
+
level = level.to_i
|
215
|
+
if level <= 0
|
216
|
+
Sqreen.log.debug('Disabling binned metrics')
|
217
|
+
PerformanceNotifications::BinnedMetrics.disable
|
218
|
+
else
|
219
|
+
Sqreen.log.warn("Unknown value for perf_level: #{level}. Treating as 1") unless level == 1
|
220
|
+
PerformanceNotifications::BinnedMetrics.enable(
|
221
|
+
metrics_engine, PERF_METRICS_PERIOD, base.to_f, factor.to_f
|
222
|
+
)
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
|
207
227
|
def setup_instrumentation(context_infos = {})
|
208
228
|
Sqreen.log.info 'setup instrumentation'
|
209
229
|
rulespack_id, rules = load_rules(context_infos)
|
@@ -282,6 +302,9 @@ module Sqreen
|
|
282
302
|
Sqreen.update_features(features)
|
283
303
|
session.request_compression = features['request_compression'] if session
|
284
304
|
self.performance_metrics_period = features['performance_metrics_period']
|
305
|
+
config_binned_metrics(features['perf_level'] || DEFAULT_PERF_LEVEL,
|
306
|
+
features['perf_base'] || DEFAULT_PERF_BASE,
|
307
|
+
features['perf_unit'] || DEFAULT_PERF_UNIT)
|
285
308
|
self.call_counts_metrics_period = features['call_counts_metrics_period']
|
286
309
|
hd = features['heartbeat_delay'].to_i
|
287
310
|
self.heartbeat_delay = hd if hd > 0
|
data/lib/sqreen/sdk.rb
CHANGED
@@ -36,13 +36,13 @@ module Sqreen
|
|
36
36
|
# For internal usage. Users are to call track() instead.
|
37
37
|
def internal_track(event_name, options = {})
|
38
38
|
properties = options[:properties]
|
39
|
-
|
39
|
+
user_identifiers = options[:user_identifiers]
|
40
40
|
timestamp = options[:timestamp] || Time.now.utc
|
41
41
|
# Not in SDK v0
|
42
42
|
# request = options[:request]
|
43
43
|
|
44
44
|
args = {}
|
45
|
-
args[:
|
45
|
+
args[:user_identifiers] = user_identifiers if user_identifiers
|
46
46
|
args[:properties] = properties if properties
|
47
47
|
|
48
48
|
Sqreen.framework.observe(
|
data/lib/sqreen/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sqreen
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.13.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sqreen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-07-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: execjs
|
@@ -81,12 +81,14 @@ files:
|
|
81
81
|
- lib/sqreen/metrics.rb
|
82
82
|
- lib/sqreen/metrics/average.rb
|
83
83
|
- lib/sqreen/metrics/base.rb
|
84
|
+
- lib/sqreen/metrics/binning.rb
|
84
85
|
- lib/sqreen/metrics/collect.rb
|
85
86
|
- lib/sqreen/metrics/sum.rb
|
86
87
|
- lib/sqreen/metrics_store.rb
|
87
88
|
- lib/sqreen/middleware.rb
|
88
89
|
- lib/sqreen/payload_creator.rb
|
89
90
|
- lib/sqreen/performance_notifications.rb
|
91
|
+
- lib/sqreen/performance_notifications/binned_metrics.rb
|
90
92
|
- lib/sqreen/performance_notifications/log.rb
|
91
93
|
- lib/sqreen/performance_notifications/log_performance.rb
|
92
94
|
- lib/sqreen/performance_notifications/metrics.rb
|
@@ -111,6 +113,7 @@ files:
|
|
111
113
|
- lib/sqreen/rules_callbacks/record_request_context.rb
|
112
114
|
- lib/sqreen/rules_callbacks/reflected_xss.rb
|
113
115
|
- lib/sqreen/rules_callbacks/regexp_rule.rb
|
116
|
+
- lib/sqreen/rules_callbacks/run_block_user_actions.rb
|
114
117
|
- lib/sqreen/rules_callbacks/run_req_start_actions.rb
|
115
118
|
- lib/sqreen/rules_callbacks/shell_env.rb
|
116
119
|
- lib/sqreen/rules_callbacks/url_matches.rb
|