sqreen-alt 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. checksums.yaml +7 -0
  2. data/CODE_OF_CONDUCT.md +22 -0
  3. data/README.md +77 -0
  4. data/Rakefile +20 -0
  5. data/lib/sqreen-alt.rb +1 -0
  6. data/lib/sqreen.rb +68 -0
  7. data/lib/sqreen/attack_detected.html +2 -0
  8. data/lib/sqreen/binding_accessor.rb +288 -0
  9. data/lib/sqreen/ca.crt +72 -0
  10. data/lib/sqreen/call_countable.rb +67 -0
  11. data/lib/sqreen/callback_tree.rb +78 -0
  12. data/lib/sqreen/callbacks.rb +100 -0
  13. data/lib/sqreen/capped_queue.rb +23 -0
  14. data/lib/sqreen/condition_evaluator.rb +235 -0
  15. data/lib/sqreen/conditionable.rb +50 -0
  16. data/lib/sqreen/configuration.rb +168 -0
  17. data/lib/sqreen/context.rb +26 -0
  18. data/lib/sqreen/deliveries/batch.rb +84 -0
  19. data/lib/sqreen/deliveries/simple.rb +39 -0
  20. data/lib/sqreen/event.rb +16 -0
  21. data/lib/sqreen/events/attack.rb +61 -0
  22. data/lib/sqreen/events/remote_exception.rb +54 -0
  23. data/lib/sqreen/events/request_record.rb +62 -0
  24. data/lib/sqreen/exception.rb +34 -0
  25. data/lib/sqreen/frameworks.rb +40 -0
  26. data/lib/sqreen/frameworks/generic.rb +446 -0
  27. data/lib/sqreen/frameworks/rails.rb +148 -0
  28. data/lib/sqreen/frameworks/rails3.rb +36 -0
  29. data/lib/sqreen/frameworks/request_recorder.rb +69 -0
  30. data/lib/sqreen/frameworks/sinatra.rb +57 -0
  31. data/lib/sqreen/frameworks/sqreen_test.rb +26 -0
  32. data/lib/sqreen/instrumentation.rb +542 -0
  33. data/lib/sqreen/log.rb +119 -0
  34. data/lib/sqreen/metrics.rb +6 -0
  35. data/lib/sqreen/metrics/average.rb +39 -0
  36. data/lib/sqreen/metrics/base.rb +45 -0
  37. data/lib/sqreen/metrics/collect.rb +22 -0
  38. data/lib/sqreen/metrics/sum.rb +20 -0
  39. data/lib/sqreen/metrics_store.rb +96 -0
  40. data/lib/sqreen/middleware.rb +34 -0
  41. data/lib/sqreen/payload_creator.rb +137 -0
  42. data/lib/sqreen/performance_notifications.rb +86 -0
  43. data/lib/sqreen/performance_notifications/log.rb +36 -0
  44. data/lib/sqreen/performance_notifications/metrics.rb +36 -0
  45. data/lib/sqreen/performance_notifications/newrelic.rb +36 -0
  46. data/lib/sqreen/remote_command.rb +93 -0
  47. data/lib/sqreen/rule_attributes.rb +26 -0
  48. data/lib/sqreen/rule_callback.rb +108 -0
  49. data/lib/sqreen/rules.rb +126 -0
  50. data/lib/sqreen/rules_callbacks.rb +29 -0
  51. data/lib/sqreen/rules_callbacks/binding_accessor_matcher.rb +77 -0
  52. data/lib/sqreen/rules_callbacks/binding_accessor_metrics.rb +79 -0
  53. data/lib/sqreen/rules_callbacks/blacklist_ips.rb +44 -0
  54. data/lib/sqreen/rules_callbacks/count_http_codes.rb +40 -0
  55. data/lib/sqreen/rules_callbacks/crawler_user_agent_matches.rb +24 -0
  56. data/lib/sqreen/rules_callbacks/crawler_user_agent_matches_metrics.rb +24 -0
  57. data/lib/sqreen/rules_callbacks/custom_error.rb +64 -0
  58. data/lib/sqreen/rules_callbacks/execjs.rb +241 -0
  59. data/lib/sqreen/rules_callbacks/headers_insert.rb +22 -0
  60. data/lib/sqreen/rules_callbacks/inspect_rule.rb +25 -0
  61. data/lib/sqreen/rules_callbacks/matcher_rule.rb +138 -0
  62. data/lib/sqreen/rules_callbacks/rails_parameters.rb +14 -0
  63. data/lib/sqreen/rules_callbacks/record_request_context.rb +39 -0
  64. data/lib/sqreen/rules_callbacks/reflected_xss.rb +254 -0
  65. data/lib/sqreen/rules_callbacks/regexp_rule.rb +36 -0
  66. data/lib/sqreen/rules_callbacks/shell_env.rb +32 -0
  67. data/lib/sqreen/rules_callbacks/url_matches.rb +25 -0
  68. data/lib/sqreen/rules_callbacks/user_agent_matches.rb +22 -0
  69. data/lib/sqreen/rules_signature.rb +151 -0
  70. data/lib/sqreen/runner.rb +365 -0
  71. data/lib/sqreen/runtime_infos.rb +138 -0
  72. data/lib/sqreen/safe_json.rb +60 -0
  73. data/lib/sqreen/sdk.rb +22 -0
  74. data/lib/sqreen/serializer.rb +46 -0
  75. data/lib/sqreen/session.rb +317 -0
  76. data/lib/sqreen/shared_storage.rb +31 -0
  77. data/lib/sqreen/stats.rb +18 -0
  78. data/lib/sqreen/version.rb +5 -0
  79. metadata +148 -0
@@ -0,0 +1,86 @@
1
+ # Copyright (c) 2015 Sqreen. All Rights Reserved.
2
+ # Please refer to our terms for more information: https://www.sqreen.io/terms.html
3
+
4
+ module Sqreen
5
+ # This module enable us to keep track of sqreen resource usage
6
+ #
7
+ # It is inspired by ActiveSupport::Notifications
8
+ #
9
+ module PerformanceNotifications
10
+ @subscriptions_all = {}
11
+ @subscriptions_regexp = {}
12
+ @subscriptions_val = Hash.new { |h, k| h[k] = [] }
13
+ @subscription_id = 0
14
+ class << self
15
+ # Subsribe to receive notificiations about an event
16
+ # returns a subscription indentitifcation
17
+ def subscribe(pattern = nil, &block)
18
+ id = (@subscription_id += 1)
19
+ case pattern
20
+ when NilClass
21
+ @subscriptions_all[id] = block
22
+ when Regexp
23
+ @subscriptions_regexp[id] = [pattern, block]
24
+ else
25
+ @subscriptions_val[pattern].push([id, block])
26
+ end
27
+ id
28
+ end
29
+
30
+ # Is there a subscriber for this key
31
+ def listen_for?(key)
32
+ return true unless @subscriptions_all.empty?
33
+ return true if @subscriptions_val.key?(key)
34
+ @subscriptions_regexp.values.any? { |r| r.first.match(key) }
35
+ end
36
+
37
+ # Instrument a call identified by key
38
+ def instrument(key, meta = {}, &block)
39
+ return yield unless listen_for?(key)
40
+ _instrument(key, meta, &block)
41
+ end
42
+
43
+ # Unsubscrube for a given subscription
44
+ def unsubscribe(subscription)
45
+ return unless @subscriptions_all.delete(subscription).nil?
46
+ return unless @subscriptions_regexp.delete(subscription).nil?
47
+ @subscriptions_val.delete_if do |_, v|
48
+ v.delete_if { |r| r.first == subscription }
49
+ v.empty?
50
+ end
51
+ end
52
+
53
+ # Unsubscribe from everything
54
+ # not threadsafe
55
+ def unsubscribe_all!
56
+ @subscriptions_all.clear
57
+ @subscriptions_regexp.clear
58
+ @subscriptions_val.clear
59
+ end
60
+
61
+ private
62
+
63
+ def notifiers_for(key)
64
+ reg = @subscriptions_regexp.values.map do |r|
65
+ r.first.match(key) && r.last
66
+ end
67
+ reg.compact!
68
+ str = []
69
+ if @subscriptions_val.key?(key)
70
+ str = @subscriptions_val[key].map(&:last)
71
+ end
72
+ @subscriptions_all.values + str + reg
73
+ end
74
+
75
+ def _instrument(key, meta)
76
+ start = Time.now
77
+ yield
78
+ ensure
79
+ stop = Time.now
80
+ notifiers_for(key).each do |callable|
81
+ callable.call(key, start, stop, meta)
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,36 @@
1
+ # Copyright (c) 2015 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
+ module PerformanceNotifications
8
+ # Log performances on the console
9
+ class Log
10
+ @subid = nil
11
+ @facility = nil
12
+ class << self
13
+ def log(event, start, finish, meta)
14
+ (@facility || Sqreen.log).debug do
15
+ meta_str = nil
16
+ meta_str = ": #{meta.inspect}" unless meta.empty?
17
+ format('%s took %.2fms%s', event, (finish - start) * 1000, meta_str)
18
+ end
19
+ end
20
+
21
+ def enable(facility = nil)
22
+ return unless @subid.nil?
23
+ @facility = facility
24
+ @subid = Sqreen::PerformanceNotifications.subscribe(nil,
25
+ &method(:log))
26
+ end
27
+
28
+ def disable
29
+ return if @subid.nil?
30
+ Sqreen::PerformanceNotifications.unsubscribe(@subid)
31
+ @subid = nil
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,36 @@
1
+ # Copyright (c) 2015 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
+ module PerformanceNotifications
8
+ # Log performances in sqreen metrics_store
9
+ class Metrics
10
+ @subid = nil
11
+ @facility = nil
12
+ class << self
13
+ EVENT_CAT = 'sqreen_time'.freeze
14
+ def log(event, start, finish, _meta)
15
+ evt = [EVENT_CAT, event, (finish - start) * 1000, finish]
16
+ Sqreen.observations_queue.push(evt)
17
+ end
18
+
19
+ def enable(metrics_engine, period = 60)
20
+ return unless @subid.nil?
21
+ metrics_engine.create_metric('name' => EVENT_CAT,
22
+ 'period' => period,
23
+ 'kind' => 'Average')
24
+ @subid = Sqreen::PerformanceNotifications.subscribe(nil,
25
+ &method(:log))
26
+ end
27
+
28
+ def disable
29
+ return if @subid.nil?
30
+ Sqreen::PerformanceNotifications.unsubscribe(@subid)
31
+ @subid = nil
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,36 @@
1
+ # Copyright (c) 2015 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
+ module PerformanceNotifications
8
+ # Log performances on the console
9
+ class NewRelic
10
+ @subid = nil
11
+
12
+ @nr_name_regexp = %r{/([^/]+)$}
13
+
14
+ class << self
15
+ def log(event, start, finish, _meta)
16
+ event_name = "Custom/Sqreen#{event.sub(@nr_name_regexp, '_\1')}"
17
+ ::NewRelic::Agent.record_metric(event_name, finish - start)
18
+ end
19
+
20
+ def enable
21
+ return unless @subid.nil?
22
+ return unless defined?(::NewRelic::Agent)
23
+ Sqreen.log.debug('Enabling New Relic reporting')
24
+ @subid = Sqreen::PerformanceNotifications.subscribe(nil,
25
+ &method(:log))
26
+ end
27
+
28
+ def disable
29
+ return if @subid.nil?
30
+ Sqreen::PerformanceNotifications.unsubscribe(@subid)
31
+ @subid = nil
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,93 @@
1
+ # Copyright (c) 2015 Sqreen. All Rights Reserved.
2
+ # Please refer to our terms for more information: https://www.sqreen.io/terms.html
3
+ require 'sqreen/log'
4
+ require 'sqreen/events/remote_exception'
5
+
6
+ module Sqreen
7
+ # Execute and sanitize remote commands
8
+ class RemoteCommand
9
+ KNOWN_COMMANDS = {
10
+ :instrumentation_enable => :setup_instrumentation,
11
+ :instrumentation_remove => :remove_instrumentation,
12
+ :rules_reload => :reload_rules,
13
+ :features_get => :features,
14
+ :features_change => :change_features,
15
+ :force_logout => :shutdown,
16
+ :paths_whitelist => :change_whitelisted_paths,
17
+ :ips_whitelist => :change_whitelisted_ips,
18
+ :get_bundle => :upload_bundle,
19
+ }.freeze
20
+
21
+ attr_reader :uuid
22
+
23
+ def initialize(json_desc)
24
+ @name = json_desc['name'].to_sym
25
+ @params = json_desc.fetch('params', [])
26
+ @uuid = json_desc['uuid']
27
+ end
28
+
29
+ def process(runner, context_infos = {})
30
+ failing = validate_command(runner)
31
+ return failing if failing
32
+ Sqreen.log.debug format('processing command %s', @name)
33
+ begin
34
+ output = runner.send(KNOWN_COMMANDS[@name], *@params, context_infos)
35
+ rescue => e
36
+ Sqreen::RemoteException.record(e)
37
+ return { :status => false, :reason => "error: #{e.inspect}" }
38
+ end
39
+ format_output(output)
40
+ end
41
+
42
+ def self.process_list(runner, commands, context_infos = {})
43
+ res_list = {}
44
+
45
+ return res_list unless commands
46
+
47
+ unless commands.is_a? Array
48
+ Sqreen.log.debug format('Wrong commands type %s', commands.class)
49
+ Sqreen.log.debug commands.inspect
50
+ return res_list
51
+ end
52
+ commands.each do |cmd_json|
53
+ Sqreen.log.debug cmd_json
54
+ cmd = RemoteCommand.new(cmd_json)
55
+ Sqreen.log.debug cmd.inspect
56
+ uuid = cmd.uuid
57
+ res_list[uuid] = cmd.process(runner, context_infos)
58
+ end
59
+ res_list
60
+ end
61
+
62
+ def to_h
63
+ {
64
+ :name => @name,
65
+ }
66
+ end
67
+
68
+ protected
69
+
70
+ def validate_command(runner)
71
+ unless KNOWN_COMMANDS.include?(@name)
72
+ msg = format("unknown command name '%s'", @name)
73
+ Sqreen.log.debug msg
74
+ return { :status => false, :reason => msg }
75
+ end
76
+ return nil if runner.respond_to?(KNOWN_COMMANDS[@name])
77
+ msg = format("not implemented '%s'", @name)
78
+ Sqreen.log.debug msg
79
+ { :status => false, :reason => msg }
80
+ end
81
+
82
+ def format_output(output)
83
+ case output
84
+ when NilClass
85
+ return { :status => false, :reason => 'nil returned' }
86
+ when TrueClass
87
+ return { :status => true }
88
+ else
89
+ return { :status => true, :output => output }
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,26 @@
1
+ # Copyright (c) 2015 Sqreen. All Rights Reserved.
2
+ # Please refer to our terms for more information: https://www.sqreen.io/terms.html
3
+
4
+ module Sqreen
5
+ module Rules
6
+ # Common field names in a rule
7
+ module Attrs
8
+ CALLBACKS = 'callbacks'.freeze
9
+ BLOCK = 'block'.freeze
10
+ TEST = 'test'.freeze
11
+ DATA = 'data'.freeze
12
+ PAYLOAD = 'payload'.freeze
13
+ NAME = 'name'.freeze
14
+ RULESPACK_ID = 'rulespack_id'.freeze
15
+ HOOKPOINT = 'hookpoint'.freeze
16
+ KLASS = 'klass'.freeze
17
+ METHOD = 'method'.freeze
18
+ CALLBACK_CLASS = 'callback_class'.freeze
19
+ METRICS = 'metrics'.freeze
20
+ CONDITIONS = 'conditions'.freeze
21
+ CALL_COUNT_INTERVAL = 'call_count_interval'.freeze
22
+
23
+ freeze
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,108 @@
1
+ # Copyright (c) 2015 Sqreen. All Rights Reserved.
2
+ # Please refer to our terms for more information: https://www.sqreen.io/terms.html
3
+
4
+ require 'sqreen/callbacks'
5
+ require 'sqreen/context'
6
+ require 'sqreen/conditionable'
7
+ require 'sqreen/call_countable'
8
+ require 'sqreen/rule_attributes'
9
+ require 'sqreen/events/attack'
10
+ require 'sqreen/events/remote_exception'
11
+ require 'sqreen/payload_creator'
12
+
13
+ # Rules defined here can be instanciated from JSON.
14
+ module Sqreen
15
+ module Rules
16
+ # Base class for callback that are initialized by rules from Sqreen
17
+ class RuleCB < CB
18
+ include Conditionable
19
+ include CallCountable
20
+ # If nothing was asked by the rule we will ask for all sections available
21
+ # These information will be pruned later when exporting in #to_hash
22
+ DEFAULT_PAYLOAD = (PayloadCreator::METHODS.keys - ['local'] + ['context']).freeze
23
+ attr_reader :test
24
+ attr_reader :payload_tpl
25
+ attr_reader :block
26
+ attr_accessor :framework
27
+
28
+ # @params klass [String] class instrumented
29
+ # @params method [String] method that was instrumented
30
+ # @params rule_hash [Hash] Rule data that govern the current behavior
31
+ def initialize(klass, method, rule_hash)
32
+ super(klass, method)
33
+ @block = rule_hash[Attrs::BLOCK] == true
34
+ @test = rule_hash[Attrs::TEST] == true
35
+ @data = rule_hash[Attrs::DATA]
36
+ @rule = rule_hash
37
+ @payload_tpl = @rule[Attrs::PAYLOAD] || DEFAULT_PAYLOAD
38
+ condition_callbacks(@rule[Attrs::CONDITIONS])
39
+ count_callback_calls(@rule[Attrs::CALL_COUNT_INTERVAL])
40
+ end
41
+
42
+ def rule_name
43
+ @rule[Attrs::NAME]
44
+ end
45
+
46
+ def rulespack_id
47
+ @rule[Attrs::RULESPACK_ID]
48
+ end
49
+
50
+ def whitelisted?
51
+ framework && !framework.whitelisted_match.nil?
52
+ end
53
+
54
+ # Recommend taking an action (optionnally adding more data/context)
55
+ #
56
+ # This will format the requested action and optionnally
57
+ # override it if it should not be taken (should not block for example)
58
+ def advise_action(action, additional_data = {})
59
+ return if action.nil? && additional_data.empty?
60
+ additional_data.merge(:status => action)
61
+ end
62
+
63
+ # Record an attack event into Sqreen system
64
+ # @param infos [Hash] Additional information about request
65
+ def record_event(infos, at = Time.now.utc)
66
+ return unless framework
67
+ payload = {
68
+ :infos => infos,
69
+ :rulespack_id => rulespack_id,
70
+ :rule_name => rule_name,
71
+ :test => test,
72
+ :time => at,
73
+ }
74
+ if payload_tpl.include?('context')
75
+ payload[:backtrace] = Sqreen::Context.new.bt
76
+ end
77
+ framework.observe(:attacks, payload, payload_tpl)
78
+ end
79
+
80
+ # Record a metric observation
81
+ # @param category [String] Name of the metric observed
82
+ # @param key [String] aggregation key
83
+ # @param observation [Object] data observed
84
+ # @param at [Time] time when observation was made
85
+ def record_observation(category, key, observation, at = Time.now.utc)
86
+ return unless framework
87
+ framework.observe(:observations, [category, key, observation, at], [], false)
88
+ end
89
+
90
+ # Record an exception that just occurred
91
+ # @param exception [Exception] Exception to send over
92
+ # @param infos [Hash] Additional contextual information
93
+ def record_exception(exception, infos = {}, at = Time.now.utc)
94
+ return unless framework
95
+ payload = {
96
+ :exception => exception,
97
+ :infos => infos,
98
+ :rulespack_id => rulespack_id,
99
+ :rule_name => rule_name,
100
+ :test => test,
101
+ :time => at,
102
+ :backtrace => exception.backtrace || Sqreen::Context.bt,
103
+ }
104
+ framework.observe(:sqreen_exceptions, payload)
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,126 @@
1
+ # Copyright (c) 2015 Sqreen. All Rights Reserved.
2
+ # Please refer to our terms for more information: https://www.sqreen.io/terms.html
3
+
4
+ require 'sqreen/log'
5
+ require 'sqreen/rule_attributes'
6
+ require 'sqreen/rules_callbacks'
7
+
8
+
9
+ ## Rules
10
+ #
11
+ # Rule example:
12
+ # {
13
+ # :class => 'ActionController::Metal',
14
+ # :method => 'dispatch',
15
+ # :arguments => {:type => 'position', :options => {:position => 1}}
16
+ # :callback_class => 'RackCB',
17
+ # }
18
+ # We instrument ActionController::Metal#dispatch. We are interested in the first
19
+ # argument. When this method is called, we will provide it's argument to the
20
+ # callback RackCB.
21
+ #
22
+ # Another option for execution is to delegate the callback to a JS helper,
23
+ # rather than to a class. The JS callback will be executed with the requested
24
+ # arguments.
25
+
26
+ module Sqreen
27
+ # Rules related method/functions
28
+ module Rules
29
+ def self::local(configuration)
30
+ # Parse and return local rules (path defined in environment)
31
+
32
+ path = configuration.get(:local_rules)
33
+ return [] unless path
34
+ begin
35
+ File.open(path) { |f| JSON.load(f) }
36
+ rescue Errno::ENOENT
37
+ Sqreen.log.error "File '#{path}' not found"
38
+ []
39
+ end
40
+ end
41
+
42
+ # Given a rule, will instantiate the related callback.
43
+ # @param hash_rule [Hash] Rules metadata
44
+ # @param instrumentation_engine [Instrumentation] Instrumentation engine
45
+ # @param metrics_store [MetricStore] Metrics storage facility
46
+ # @param verifier [SqreenSignedVerifier] Signed verifier
47
+ def self::cb_from_rule(hash_rule, instrumentation_engine=nil, metrics_store = nil, verifier = nil)
48
+ # Check rules signature
49
+ if verifier
50
+ raise InvalidSignatureException unless verifier.verify(hash_rule)
51
+ end
52
+
53
+ hook = hash_rule[Attrs::HOOKPOINT]
54
+ klass = hook[Attrs::KLASS]
55
+
56
+ # The instrumented class can be from anywhere
57
+ instr_class = Rules.walk_const_get klass
58
+
59
+ if instr_class.nil?
60
+ rule_name = hash_rule[Attrs::NAME]
61
+ Sqreen.log.debug { "#{klass} does not exists. Skipping #{rule_name}" }
62
+ return nil
63
+ end
64
+
65
+ instr_method = hook[Attrs::METHOD]
66
+ instr_method = instr_method.to_sym
67
+ if instrumentation_engine &&
68
+ !instrumentation_engine.valid_method?(instr_class, instr_method)
69
+
70
+ Sqreen.log.debug { "#{instr_method} does not exist on #{klass} Skipping #{rule_name}" }
71
+ return nil
72
+ end
73
+
74
+ cbname = hook[Attrs::CALLBACK_CLASS]
75
+
76
+ cb_class = nil
77
+ js = hash_rule[Attrs::CALLBACKS]
78
+ cb_class = ExecJSCB if js
79
+
80
+ if cbname && Rules.const_defined?(cbname)
81
+ # Only load callbacks from sqreen
82
+ cb_class = Rules.const_get(cbname)
83
+ end
84
+
85
+ if cb_class.nil?
86
+ Sqreen.log.debug "Cannot setup #{cbname.inspect} [#{rule_name}]"
87
+ return nil
88
+ end
89
+
90
+ unless cb_class.ancestors.include?(RuleCB)
91
+ Sqreen.log.debug "#{cb_class} does not inherit from RuleCB"
92
+ return nil
93
+ end
94
+
95
+ if metrics_store
96
+ (hash_rule[Attrs::METRICS] || []).each do |metric|
97
+ metrics_store.create_metric(metric)
98
+ end
99
+ end
100
+
101
+ cb_class.new(instr_class, instr_method, hash_rule)
102
+ rescue => e
103
+ rule_name = nil
104
+ rulespack_id = nil
105
+ if hash_rule.respond_to?(:[])
106
+ rule_name = hash_rule[Attrs::NAME]
107
+ rulespack_id = hash_rule[Attrs::RULESPACK_ID]
108
+ end
109
+ Sqreen::RemoteException.record(
110
+ 'exception' => e,
111
+ 'rulespack_id' => rulespack_id,
112
+ 'rule_name' => rule_name)
113
+ Sqreen.log.debug("Creating cb from rule #{rule_name} failed (#{e.inspect})")
114
+ nil
115
+ end
116
+
117
+ def self::walk_const_get(str)
118
+ obj = Object
119
+ str.split('::').compact.each do |part|
120
+ return nil unless obj.const_defined?(part)
121
+ obj = obj.const_get(part)
122
+ end
123
+ obj
124
+ end
125
+ end
126
+ end