sqreen-alt 1.10.0

Sign up to get free protection for your applications and to get access to all the features.
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