sqreen 1.15.0-java → 1.15.5-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/lib/sqreen/actions.rb +114 -43
  3. data/lib/sqreen/callback_tree.rb +16 -3
  4. data/lib/sqreen/callbacks.rb +7 -34
  5. data/lib/sqreen/capped_queue.rb +5 -1
  6. data/lib/sqreen/configuration.rb +4 -0
  7. data/lib/sqreen/deliveries/batch.rb +7 -4
  8. data/lib/sqreen/event.rb +4 -0
  9. data/lib/sqreen/events/request_record.rb +40 -7
  10. data/lib/sqreen/frameworks/generic.rb +0 -2
  11. data/lib/sqreen/frameworks/request_recorder.rb +14 -1
  12. data/lib/sqreen/instrumentation.rb +57 -33
  13. data/lib/sqreen/js/mini_racer_adapter.rb +46 -8
  14. data/lib/sqreen/metrics/average.rb +1 -1
  15. data/lib/sqreen/metrics/base.rb +4 -2
  16. data/lib/sqreen/metrics/binning.rb +3 -2
  17. data/lib/sqreen/metrics/collect.rb +1 -1
  18. data/lib/sqreen/metrics/sum.rb +1 -1
  19. data/lib/sqreen/metrics_store.rb +10 -5
  20. data/lib/sqreen/mono_time.rb +18 -0
  21. data/lib/sqreen/performance_notifications.rb +13 -38
  22. data/lib/sqreen/performance_notifications/binned_metrics.rb +12 -14
  23. data/lib/sqreen/performance_notifications/log.rb +6 -1
  24. data/lib/sqreen/performance_notifications/log_performance.rb +3 -1
  25. data/lib/sqreen/performance_notifications/metrics.rb +6 -3
  26. data/lib/sqreen/performance_notifications/newrelic.rb +6 -2
  27. data/lib/sqreen/remote_command.rb +26 -0
  28. data/lib/sqreen/rule_attributes.rb +1 -0
  29. data/lib/sqreen/rule_callback.rb +38 -0
  30. data/lib/sqreen/rules_callbacks/binding_accessor_matcher.rb +3 -2
  31. data/lib/sqreen/rules_callbacks/blacklist_ips.rb +1 -1
  32. data/lib/sqreen/rules_callbacks/run_block_user_actions.rb +1 -1
  33. data/lib/sqreen/rules_callbacks/run_req_start_actions.rb +8 -2
  34. data/lib/sqreen/runner.rb +11 -8
  35. data/lib/sqreen/sdk.rb +7 -1
  36. data/lib/sqreen/session.rb +4 -0
  37. data/lib/sqreen/trie.rb +274 -0
  38. data/lib/sqreen/version.rb +1 -1
  39. metadata +4 -2
@@ -3,6 +3,7 @@
3
3
 
4
4
  require 'sqreen/exception'
5
5
  require 'sqreen/metrics'
6
+ require 'sqreen/mono_time'
6
7
 
7
8
  module Sqreen
8
9
  # This store and register metrics
@@ -17,6 +18,7 @@ module Sqreen
17
18
  class AlreadyRegisteredMetric < Sqreen::Exception
18
19
  end
19
20
 
21
+ # definition keys
20
22
  NAME_KEY = 'name'.freeze
21
23
  KIND_KEY = 'kind'.freeze
22
24
  PERIOD_KEY = 'period'.freeze
@@ -29,7 +31,7 @@ module Sqreen
29
31
 
30
32
  def initialize
31
33
  @store = []
32
- @metrics = {}
34
+ @metrics = {} # name => (metric, period, start)
33
35
  end
34
36
 
35
37
  # Definition contains a name,period and aggregate at least
@@ -54,17 +56,17 @@ module Sqreen
54
56
  @metrics.key?(name)
55
57
  end
56
58
 
59
+ # @params at [Time] when is the store emptied
57
60
  def update(name, at, key, value)
58
- at = at.utc
59
61
  metric, period, start = @metrics[name]
60
62
  raise UnregisteredMetric, "Unknown metric #{name}" unless metric
61
63
  next_sample(name, at) if start.nil? || (start + period) < at
62
- metric.update(at, key, value)
64
+ metric.update(key, value)
63
65
  end
64
66
 
65
67
  # Drains every metrics and returns the store content
66
68
  # @params at [Time] when is the store emptied
67
- def publish(flush = true, at = Time.now.utc)
69
+ def publish(flush = true, at = Sqreen.time)
68
70
  @metrics.each do |name, (_, period, start)|
69
71
  next_sample(name, at) if flush || !start.nil? && (start + period) < at
70
72
  end
@@ -78,10 +80,13 @@ module Sqreen
78
80
  def next_sample(name, at)
79
81
  metric = @metrics[name][0]
80
82
  r = metric.next_sample(at)
81
- @metrics[name][2] = at # start
83
+ @metrics[name][2] = at # new start
82
84
  if r
83
85
  r[NAME_KEY] = name
84
86
  obs = r[Metric::OBSERVATION_KEY]
87
+ start_of_mono = Time.now.utc - Sqreen.time
88
+ r[Metric::START_KEY] = start_of_mono + r[Metric::START_KEY]
89
+ r[Metric::FINISH_KEY] = start_of_mono + r[Metric::FINISH_KEY]
85
90
  @store << r if obs && (!obs.respond_to?(:empty?) || !obs.empty?)
86
91
  end
87
92
  r
@@ -0,0 +1,18 @@
1
+ module Sqreen
2
+ has_mono_time = begin
3
+ Process.clock_gettime Process::CLOCK_MONOTONIC
4
+ true
5
+ rescue StandardError
6
+ false
7
+ end
8
+
9
+ if has_mono_time
10
+ def self.time
11
+ Process.clock_gettime Process::CLOCK_MONOTONIC
12
+ end
13
+ else
14
+ def self.time
15
+ Time.now.to_f
16
+ end
17
+ end
18
+ end
@@ -1,12 +1,7 @@
1
1
  # Copyright (c) 2015 Sqreen. All Rights Reserved.
2
2
  # Please refer to our terms for more information: https://www.sqreen.io/terms.html
3
3
 
4
- begin
5
- Process.clock_gettime Process::CLOCK_MONOTONIC
6
- SQREEN_MONO_TIME = Process::CLOCK_MONOTONIC
7
- rescue StandardError
8
- SQREEN_MONO_TIME = nil
9
- end
4
+ require 'sqreen/mono_time'
10
5
 
11
6
  module Sqreen
12
7
  # This module enable us to keep track of sqreen resource usage
@@ -17,39 +12,29 @@ module Sqreen
17
12
  @subscriptions_all = {}
18
13
  @subscription_id = 0
19
14
  class << self
20
- # Subsribe to receive notificiations about an event
21
- # returns a subscription indentitifcation
15
+ # Subscribe to receive notifications about an event
16
+ # returns a subscription identification
22
17
  def subscribe(&block)
23
18
  id = (@subscription_id += 1)
24
19
  @subscriptions_all[id] = block
25
20
  id
26
21
  end
27
22
 
28
- if SQREEN_MONO_TIME
29
- def time
30
- Process.clock_gettime(SQREEN_MONO_TIME)
31
- end
32
- else
33
- def time
34
- Time.now
35
- end
36
- end
37
-
38
23
  # Is there a subscriber
39
24
  def listen_for?
40
25
  !@subscriptions_all.empty?
41
26
  end
42
27
 
43
28
  # Instrument a call identified by key
44
- def instrument(key, meta = {}, &block)
29
+ def instrument(rule, cb, meta = {}, &block)
45
30
  return yield unless listen_for?
46
- _instrument(key, meta, &block)
31
+ _instrument(rule, cb, meta, &block)
47
32
  end
48
33
 
49
- def notify(key, start, stop, meta = {})
34
+ def notify(rule, cb, start, stop, meta = {})
50
35
  return unless listen_for?
51
36
  notifiers.each do |callable|
52
- callable.call(key, start, stop, meta)
37
+ callable.call(rule, cb, start, stop, meta)
53
38
  end
54
39
  end
55
40
 
@@ -70,22 +55,12 @@ module Sqreen
70
55
  @subscriptions_all.values
71
56
  end
72
57
 
73
- if SQREEN_MONO_TIME
74
- def _instrument(key, meta)
75
- start = Process.clock_gettime(SQREEN_MONO_TIME)
76
- yield
77
- ensure
78
- stop = Process.clock_gettime(SQREEN_MONO_TIME)
79
- notify(key, start, stop, meta)
80
- end
81
- else
82
- def _instrument(key, meta)
83
- start = Time.now
84
- yield
85
- ensure
86
- stop = Time.now
87
- notify(key, start, stop, meta)
88
- end
58
+ def _instrument(rule, cb, meta)
59
+ start = Sqreen.time
60
+ yield
61
+ ensure
62
+ stop = Sqreen.time
63
+ notify(rule, cb, start, stop, meta)
89
64
  end
90
65
  end
91
66
  end
@@ -16,8 +16,6 @@ module Sqreen
16
16
  EVENT_TOTAL_TIME = 'sq'.freeze # sqreen total overhead callback time
17
17
  EVENT_PERCENT = 'pct'.freeze # sqreen total overhead percent of time
18
18
 
19
- EVT_NAME_REGEXP = %r{\ACallbacks/([^/]+)/([^/]+)\z}
20
-
21
19
  # @param metrics_store [Sqreen::MetricsStore]
22
20
  def initialize(metrics_store, period, perf_metric_opts, perf_metric_percent_opts)
23
21
  @metrics_store = metrics_store
@@ -49,32 +47,28 @@ module Sqreen
49
47
  @subid = nil
50
48
  end
51
49
 
52
- def log(event, start, finish, _meta)
53
- return unless event =~ EVT_NAME_REGEXP
54
- rule, cb = Regexp.last_match.captures
55
-
50
+ def log(rule, cb, start, finish, _meta)
56
51
  metric_name = "sq.#{rule}.#{cb}"
57
52
  ensure_metric(metric_name)
58
53
 
59
- finish_time = SQREEN_MONO_TIME ? Time.now.utc : finish
60
54
  time_millis = (finish - start) * 1000
61
55
  # Ensure we always have a timings if we somehow missed the request start
62
- SharedStorage[:sqreen_request_time] ||= 0
63
- SharedStorage[:sqreen_request_time] += time_millis
64
- metrics_store.update(metric_name, finish_time, nil, time_millis)
56
+ SharedStorage[:sqreen_request_time] =
57
+ (SharedStorage[:sqreen_request_time] || 0) + time_millis
58
+ metrics_store.update(metric_name, finish, nil, time_millis)
65
59
  end
66
60
 
67
61
  def start_request
68
- SharedStorage[:request_start_time] = PerformanceNotifications.time
62
+ SharedStorage[:request_start_time] = Sqreen.time
69
63
  SharedStorage[:sqreen_request_time] = 0.0
70
64
  end
71
65
 
72
66
  def finish_request
73
67
  start_time = SharedStorage[:request_start_time]
74
- finish_time = PerformanceNotifications.time
68
+ finish_time = Sqreen.time
75
69
  duration_millis = (finish_time - start_time) * 1000
76
70
 
77
- finish_time_obj = SQREEN_MONO_TIME ? Time.now.utc : finish_time
71
+ finish_time_obj = Time.now.utc
78
72
  # format of evt is [cat, key, value, timestamp]
79
73
  Sqreen.observations_queue.push(
80
74
  [EVENT_REQ, nil, duration_millis, finish_time_obj]
@@ -108,7 +102,11 @@ module Sqreen
108
102
  class << self
109
103
  # @return [Sqreen::PerformanceNotifications::BinnedMetrics]
110
104
  attr_reader :instance
111
- def enable(metrics_store, period = 60, base = DEFAULT_PERF_BASE, factor = DEFAULT_PERF_UNIT, base_pct = DEFAULT_PERF_PCT_BASE, factor_pct = DEFAULT_PERF_PCT_UNIT)
105
+ def enable(metrics_store, period = 60,
106
+ base = DEFAULT_PERF_BASE,
107
+ factor = DEFAULT_PERF_UNIT,
108
+ base_pct = DEFAULT_PERF_PCT_BASE,
109
+ factor_pct = DEFAULT_PERF_PCT_UNIT)
112
110
  disable
113
111
  @instance = new(metrics_store, period,
114
112
  {'base'=> base, 'factor' => factor},
@@ -10,14 +10,19 @@ module Sqreen
10
10
  @subid = nil
11
11
  @facility = nil
12
12
  class << self
13
- def log(event, start, finish, meta)
13
+ def log(rule, cb, start, finish, meta)
14
14
  (@facility || Sqreen.log).debug do
15
15
  meta_str = nil
16
16
  meta_str = ": #{meta.inspect}" unless meta.empty?
17
+ event = event_name(rule, cb)
17
18
  format('%s took %.2fms%s', event, (finish - start) * 1000, meta_str)
18
19
  end
19
20
  end
20
21
 
22
+ def event_name(rule, cb)
23
+ "Callbacks/#{rule}/#{cb}"
24
+ end
25
+
21
26
  def enable(facility = nil)
22
27
  return unless @subid.nil?
23
28
  @facility = facility
@@ -2,6 +2,7 @@
2
2
  # Please refer to our terms for more information: https://www.sqreen.io/terms.html
3
3
 
4
4
  require 'sqreen/performance_notifications'
5
+ require 'sqreen/performance_notifications/log'
5
6
 
6
7
  module Sqreen
7
8
  module PerformanceNotifications
@@ -23,7 +24,8 @@ module Sqreen
23
24
  SharedStorage.set(:log_performance_timings, value)
24
25
  end
25
26
 
26
- def log(event, start, finish, _meta)
27
+ def log(rule, cb, start, finish, _meta)
28
+ event = event_name(rule, cb)
27
29
  timings << [event, start, finish]
28
30
  end
29
31
 
@@ -2,17 +2,20 @@
2
2
  # Please refer to our terms for more information: https://www.sqreen.io/terms.html
3
3
 
4
4
  require 'sqreen/performance_notifications'
5
+ require 'sqreen/performance_notifications/log'
6
+ require 'sqreen/runner'
5
7
 
6
8
  module Sqreen
7
9
  module PerformanceNotifications
8
10
  # Log performances in sqreen metrics_store
9
- class Metrics
11
+ class Metrics < Log
10
12
  @subid = nil
11
13
  @facility = nil
12
14
  class << self
13
15
  EVENT_CAT = 'sqreen_time'.freeze
14
- def log(event, start, finish, _meta)
15
- evt = [EVENT_CAT, event, (finish - start) * 1000, SQREEN_MONO_TIME ? Time.now.utc : finish]
16
+ def log(rule, cb, start, finish, _meta)
17
+ event = event_name(rule, cb)
18
+ evt = [EVENT_CAT, event, (finish - start) * 1000, Time.now.utc]
16
19
  Sqreen.observations_queue.push(evt)
17
20
  end
18
21
 
@@ -2,11 +2,14 @@
2
2
  # Please refer to our terms for more information: https://www.sqreen.io/terms.html
3
3
 
4
4
  require 'sqreen/performance_notifications'
5
+ require 'sqreen/performance_notifications/log'
6
+ require 'sqreen/log'
7
+ require 'sqreen/shared_storage'
5
8
 
6
9
  module Sqreen
7
10
  module PerformanceNotifications
8
11
  # Log performances on the console
9
- class NewRelic
12
+ class NewRelic < Log
10
13
  @subid = nil
11
14
  @level = 0
12
15
 
@@ -24,7 +27,8 @@ module Sqreen
24
27
  SharedStorage.set(:log_performance_nr_timings, value)
25
28
  end
26
29
 
27
- def log(event, start, finish, _meta)
30
+ def log(rule, cb, start, finish, _meta)
31
+ event = event_name(rule, cb)
28
32
  timings << [event, start, finish]
29
33
  end
30
34
 
@@ -59,6 +59,7 @@ module Sqreen
59
59
  Sqreen.log.debug { commands.inspect }
60
60
  return res_list
61
61
  end
62
+ commands = coalesce_reloads(commands, res_list)
62
63
  commands.each do |cmd_json|
63
64
  Sqreen.log.debug { cmd_json }
64
65
  cmd = RemoteCommand.new(cmd_json)
@@ -69,6 +70,31 @@ module Sqreen
69
70
  res_list
70
71
  end
71
72
 
73
+ # will need changes if we ever distinguish forced/soft reloads
74
+ # ('force' parameter in the command)
75
+ def self.coalesce_reloads(commands, res_list)
76
+ new_commands = []
77
+ saw_rules_reload = false
78
+ commands.reverse_each do |cmd_json|
79
+ name = cmd_json['name']
80
+ unless name == 'rules_reload'
81
+ new_commands.unshift cmd_json
82
+ next
83
+ end
84
+
85
+ if saw_rules_reload
86
+ res_list[cmd_json['uuid']] =
87
+ { :status => false, :reason => "redundant rules_reload ignored" }
88
+ else
89
+ saw_rules_reload = true
90
+ new_commands.unshift cmd_json
91
+ next
92
+ end
93
+ end
94
+
95
+ new_commands
96
+ end
97
+
72
98
  def to_h
73
99
  {
74
100
  :name => @name,
@@ -19,6 +19,7 @@ module Sqreen
19
19
  METRICS = 'metrics'.freeze
20
20
  CONDITIONS = 'conditions'.freeze
21
21
  CALL_COUNT_INTERVAL = 'call_count_interval'.freeze
22
+ PRIORITY = 'priority'.freeze
22
23
 
23
24
  freeze
24
25
  end
@@ -47,6 +47,44 @@ module Sqreen
47
47
  @rule[Attrs::RULESPACK_ID]
48
48
  end
49
49
 
50
+ def priority
51
+ @rule[Attrs::PRIORITY] || super
52
+ end
53
+
54
+ # Record an attack event into Sqreen system
55
+ # @param infos [Hash] Additional information about request
56
+ def record_event(infos, at = Time.now.utc)
57
+ return unless framework
58
+ payload = {
59
+ :infos => infos,
60
+ :rulespack_id => rulespack_id,
61
+ :rule_name => rule_name,
62
+ :test => test,
63
+ :time => at,
64
+ }
65
+ if payload_tpl.include?('context')
66
+ payload[:backtrace] = Sqreen::Context.new.bt
67
+ end
68
+ framework.observe(:attacks, payload, payload_tpl)
69
+ end
70
+
71
+ # Record an exception that just occurred
72
+ # @param exception [Exception] Exception to send over
73
+ # @param infos [Hash] Additional contextual information
74
+ def record_exception(exception, infos = {}, at = Time.now.utc)
75
+ return unless framework
76
+ payload = {
77
+ :exception => exception,
78
+ :infos => infos,
79
+ :rulespack_id => rulespack_id,
80
+ :rule_name => rule_name,
81
+ :test => test,
82
+ :time => at,
83
+ :backtrace => exception.backtrace || Sqreen::Context.bt,
84
+ }
85
+ framework.observe(:sqreen_exceptions, payload)
86
+ end
87
+
50
88
  # Recommend taking an action (optionnally adding more data/context)
51
89
  #
52
90
  # This will format the requested action and optionnally
@@ -3,6 +3,7 @@
3
3
 
4
4
  require 'sqreen/rule_callback'
5
5
  require 'sqreen/binding_accessor'
6
+ require 'sqreen/mono_time'
6
7
  require 'sqreen/rules_callbacks/matcher_rule'
7
8
 
8
9
  module Sqreen
@@ -49,7 +50,7 @@ module Sqreen
49
50
 
50
51
  def pre(inst, args, budget = nil, &_block)
51
52
  unless budget.nil?
52
- finish = budget + Sqreen::PerformanceNotifications.time.to_f
53
+ finish = budget + Sqreen.time
53
54
  end
54
55
  resol_cache = Hash.new do |hash, accessor|
55
56
  hash[accessor] = accessor.resolve(binding, framework, inst, args)
@@ -61,7 +62,7 @@ module Sqreen
61
62
  next unless val.respond_to?(:each)
62
63
  next if val.respond_to?(:seek)
63
64
  val.each do |v|
64
- if !budget.nil? && Sqreen::PerformanceNotifications.time.to_f > finish
65
+ if !budget.nil? && Sqreen.time > finish
65
66
  return nil
66
67
  end
67
68
  next if !v.is_a?(String) || (!matcher.min_size.nil? && v.size < matcher.min_size)
@@ -23,7 +23,7 @@ module Sqreen
23
23
  return unless found
24
24
  Sqreen.log.debug { "Found blacklisted IP #{ip} - found: #{found}" }
25
25
  record_observation('blacklisted', found, 1)
26
- advise_action(:raise)
26
+ advise_action(:raise, :skip_rem_cbs => true)
27
27
  end
28
28
 
29
29
  protected