sqreen 0.7.01461158029-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +7 -0
  2. data/CODE_OF_CONDUCT.md +22 -0
  3. data/README.md +77 -0
  4. data/Rakefile +40 -0
  5. data/lib/sqreen.rb +67 -0
  6. data/lib/sqreen/binding_accessor.rb +184 -0
  7. data/lib/sqreen/ca.crt +72 -0
  8. data/lib/sqreen/callback_tree.rb +78 -0
  9. data/lib/sqreen/callbacks.rb +120 -0
  10. data/lib/sqreen/capped_queue.rb +23 -0
  11. data/lib/sqreen/condition_evaluator.rb +169 -0
  12. data/lib/sqreen/conditionable.rb +50 -0
  13. data/lib/sqreen/configuration.rb +151 -0
  14. data/lib/sqreen/context.rb +22 -0
  15. data/lib/sqreen/deliveries/batch.rb +80 -0
  16. data/lib/sqreen/deliveries/simple.rb +36 -0
  17. data/lib/sqreen/detect.rb +14 -0
  18. data/lib/sqreen/detect/shell_injection.rb +61 -0
  19. data/lib/sqreen/detect/sql_injection.rb +115 -0
  20. data/lib/sqreen/event.rb +16 -0
  21. data/lib/sqreen/events/attack.rb +60 -0
  22. data/lib/sqreen/events/remote_exception.rb +53 -0
  23. data/lib/sqreen/exception.rb +31 -0
  24. data/lib/sqreen/frameworks.rb +40 -0
  25. data/lib/sqreen/frameworks/generic.rb +243 -0
  26. data/lib/sqreen/frameworks/rails.rb +155 -0
  27. data/lib/sqreen/frameworks/rails3.rb +36 -0
  28. data/lib/sqreen/frameworks/sinatra.rb +34 -0
  29. data/lib/sqreen/frameworks/sqreen_test.rb +26 -0
  30. data/lib/sqreen/instrumentation.rb +504 -0
  31. data/lib/sqreen/log.rb +116 -0
  32. data/lib/sqreen/metrics.rb +6 -0
  33. data/lib/sqreen/metrics/average.rb +39 -0
  34. data/lib/sqreen/metrics/base.rb +41 -0
  35. data/lib/sqreen/metrics/collect.rb +22 -0
  36. data/lib/sqreen/metrics/sum.rb +20 -0
  37. data/lib/sqreen/metrics_store.rb +94 -0
  38. data/lib/sqreen/parsers/sql.rb +98 -0
  39. data/lib/sqreen/parsers/sql_tokenizer.rb +266 -0
  40. data/lib/sqreen/parsers/unix.rb +110 -0
  41. data/lib/sqreen/payload_creator.rb +132 -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 +82 -0
  47. data/lib/sqreen/rule_attributes.rb +25 -0
  48. data/lib/sqreen/rule_callback.rb +97 -0
  49. data/lib/sqreen/rules.rb +116 -0
  50. data/lib/sqreen/rules_callbacks.rb +29 -0
  51. data/lib/sqreen/rules_callbacks/binding_accessor_metrics.rb +79 -0
  52. data/lib/sqreen/rules_callbacks/count_http_codes.rb +18 -0
  53. data/lib/sqreen/rules_callbacks/crawler_user_agent_matches.rb +24 -0
  54. data/lib/sqreen/rules_callbacks/crawler_user_agent_matches_metrics.rb +25 -0
  55. data/lib/sqreen/rules_callbacks/execjs.rb +136 -0
  56. data/lib/sqreen/rules_callbacks/headers_insert.rb +20 -0
  57. data/lib/sqreen/rules_callbacks/inspect_rule.rb +20 -0
  58. data/lib/sqreen/rules_callbacks/matcher_rule.rb +103 -0
  59. data/lib/sqreen/rules_callbacks/rails_parameters.rb +14 -0
  60. data/lib/sqreen/rules_callbacks/record_request_context.rb +23 -0
  61. data/lib/sqreen/rules_callbacks/reflected_xss.rb +40 -0
  62. data/lib/sqreen/rules_callbacks/regexp_rule.rb +36 -0
  63. data/lib/sqreen/rules_callbacks/shell.rb +33 -0
  64. data/lib/sqreen/rules_callbacks/shell_env.rb +32 -0
  65. data/lib/sqreen/rules_callbacks/sql.rb +41 -0
  66. data/lib/sqreen/rules_callbacks/system_shell.rb +25 -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 +142 -0
  70. data/lib/sqreen/runner.rb +312 -0
  71. data/lib/sqreen/runtime_infos.rb +127 -0
  72. data/lib/sqreen/session.rb +340 -0
  73. data/lib/sqreen/stats.rb +18 -0
  74. data/lib/sqreen/version.rb +6 -0
  75. metadata +143 -0
@@ -0,0 +1,78 @@
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
+
6
+ module Sqreen
7
+ class CBTree
8
+ include Enumerable
9
+ # Callbacks tree:
10
+ # class
11
+ # methods
12
+ # position
13
+
14
+ def initialize
15
+ @by_class = {}
16
+ end
17
+
18
+ def add(cb)
19
+ @by_class[cb.klass] = {} unless @by_class[cb.klass]
20
+
21
+ cb_klass = @by_class[cb.klass]
22
+ unless cb_klass[cb.method]
23
+ cb_klass[cb.method] = { :pre => [], :post => [], :failing => [] }
24
+ end
25
+
26
+ methods = cb_klass[cb.method]
27
+
28
+ methods[:pre] << cb if cb.pre?
29
+ methods[:post] << cb if cb.post?
30
+ methods[:failing] << cb if cb.failing?
31
+ end
32
+
33
+ def remove(cb)
34
+ types = @by_class[cb.klass][cb.method]
35
+
36
+ types[:pre].delete cb if cb.pre?
37
+ types[:post].delete cb if cb.post?
38
+ types[:failing].delete cb if cb.failing?
39
+ end
40
+
41
+ def get(klass, method, type = nil)
42
+ k = @by_class[klass]
43
+ unless k
44
+ log(format('Error: no cb registered for class %s (%s)', klass.inspect, klass.class))
45
+ log(inspect)
46
+ return []
47
+ end
48
+ cbs = k[method]
49
+ unless cbs
50
+ log(format('Error: no cbs registered for method %s.%s', klass, method))
51
+ log(inspect)
52
+ return []
53
+ end
54
+
55
+ if type.nil?
56
+ res = Set.new
57
+ cbs.values.each do |v|
58
+ res += v
59
+ end
60
+ return res.to_a
61
+ else
62
+ return cbs[type]
63
+ end
64
+ end
65
+
66
+ def each
67
+ @by_class.each do |_klass, values|
68
+ values.each do |_method, cbs|
69
+ cbs.values.each do |cb_ary|
70
+ cb_ary.each do |cb|
71
+ yield cb
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,120 @@
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 'set'
5
+
6
+ module Sqreen
7
+ module SharedStorage
8
+ @@shared = {}
9
+
10
+ def self::get(key, default = nil)
11
+ h = @@shared[Thread.current]
12
+ return h.fetch(key, default) if h
13
+ default
14
+ end
15
+
16
+ def self::set(key, obj)
17
+ main_key = Thread.current
18
+ @@shared[main_key] = {} unless @@shared.key? main_key
19
+ @@shared[main_key][key] = obj
20
+ end
21
+
22
+ def self.clear
23
+ @@shared.delete(Thread.current)
24
+ end
25
+
26
+ def self.inc(value)
27
+ set(value, get(value, 0) + 1)
28
+ end
29
+
30
+ def self.dec(value)
31
+ set(value, get(value, 0) - 1)
32
+ end
33
+ end
34
+
35
+ class CB
36
+ # Callback class.
37
+ #
38
+ # Three methods can be defined:
39
+ # - pre(*args, &block)
40
+ # To be called prior to the hooked method.
41
+ # - post(return_value, *args, &block)
42
+ # To be called after the hooked method. The return_value argument is
43
+ # the value returned by the hooked method.
44
+ # - failing(exception, ...)
45
+ # To be called when the method raise
46
+ # The method pre, post and exception may return nil or a Hash.
47
+ # - nil: the original method is called and the callback has no further
48
+ # effect
49
+ # - { :status => :skip }: we skip the original method call
50
+ # - { :status => :raise}:
51
+ #
52
+ # - nil: the original return value is returned, as if coallback had no
53
+ # effect
54
+ # - { :status => :raise}:
55
+ # - { :status => :override }:
56
+ #
57
+ # - nil: reraise
58
+ # - { :status => :reraise }: reraise
59
+ # - { :status => :override }: eat exception
60
+ # - { :retry => :retry }: try the block again
61
+
62
+ attr_reader :klass, :method
63
+
64
+ def initialize(klass, method)
65
+ @method = method
66
+ @klass = klass
67
+
68
+ @has_pre = respond_to? :pre
69
+ @has_post = respond_to? :post
70
+ @has_failing = respond_to? :failing
71
+
72
+ raise(Sqreen::Exception, 'No callback provided') unless @has_pre || @has_post || @has_failing
73
+ end
74
+
75
+ def pre?
76
+ @has_pre
77
+ end
78
+
79
+ def post?
80
+ @has_post
81
+ end
82
+
83
+ def failing?
84
+ @has_failing
85
+ end
86
+
87
+ def to_s
88
+ format('#<%s: %s.%s>', self.class, @klass, @method)
89
+ end
90
+ end
91
+ # target_method, position, callback, callback class
92
+
93
+ class DefaultCB < CB
94
+ def pre(_inst, *args, &_block)
95
+ Sqreen.log.debug "<< #{@klass} #{@method} #{Thread.current}"
96
+ Sqreen.log.debug args.join ' '
97
+ # log params
98
+ end
99
+
100
+ def post(_rv, _inst, *_args, &_block)
101
+ # log "#{rv}"
102
+ Sqreen.log.debug ">> #{@klass} #{@method} #{Thread.current}"
103
+ end
104
+ end
105
+
106
+ class RunWhenCalledCB < CB
107
+ def initialize(klass, method, &block)
108
+ super(klass, method)
109
+
110
+ raise 'missing block' unless block_given?
111
+ @block = block
112
+ end
113
+
114
+ def pre(_inst, *_args, &_block)
115
+ # FIXME: implement this removal
116
+ @remove_me = true
117
+ @block.call
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,23 @@
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
+ # A simple size limited queue.
6
+ # When trying to enqueue more than the capacity
7
+ # the older elements will get thrown
8
+ class CappedQueue < Queue
9
+ attr_reader :capacity
10
+
11
+ def initialize(capacity)
12
+ @capacity = capacity
13
+ super()
14
+ end
15
+
16
+ alias original_push push
17
+
18
+ def push(value)
19
+ pop until size < @capacity
20
+ original_push(value)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,169 @@
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/binding_accessor'
5
+ require 'sqreen/exception'
6
+
7
+ # Evaluate a condition, resolving literals using BindingAccessor.
8
+ #
9
+ # { "%and" => ["true", "true"] } -> true
10
+ # { "%or" => ["true", "false"] } -> true
11
+ # { "%and" => ["false", "true"] } -> false
12
+ #
13
+ # { "%equal" => ["coucou", "#.args[0]"] } -> "coucou" == args[0]
14
+ # { "%hash_val_include" => ["toto is a small guy", "#.request_params"] } ->
15
+ # true if one value of request params in included
16
+ # in the sentence 'toto is a small guy'.
17
+ #
18
+ # Combine expressions:
19
+ # { "%or" =>
20
+ # [
21
+ # { "%hash_val_include" => ["AAA", "#.request_params"] },
22
+ # { "%hash_val_include" => ["BBB", "#.request_params"] },
23
+ # ]
24
+ # }
25
+ # will return true if one of the request_params include either AAA or BBB.
26
+ #
27
+ class ConditionEvaluator
28
+ # Predicate: Is value deeply included in hash
29
+ # @params value [Object] object to find
30
+ # @params hash [Hash] Hash to search into
31
+ # @params min_value_size [Fixnum] to compare against
32
+ def self.hash_val_include?(value, hash, min_value_size, rem = 10)
33
+ return false if rem <= 0
34
+ hash.any? do |_k, hval|
35
+ case hval
36
+ when Hash
37
+ ConditionEvaluator.hash_val_include?(value, hval, min_value_size, rem - 1)
38
+ when NilClass
39
+ false
40
+ else
41
+ return false if hval.respond_to?(:empty?) && hval.empty?
42
+ v = hval.to_s
43
+ return false if v.size < min_value_size
44
+ value.to_s.include?(v)
45
+ end
46
+ end
47
+ end
48
+
49
+ # Initialize evaluator
50
+ # @param cond [Hash] condition Hash
51
+ def initialize(cond)
52
+ unless cond == true || cond == false
53
+ unless cond.respond_to? :each
54
+ raise(Sqreen::Exception, "cond should be a Hash (was #{cond.class})")
55
+ end
56
+ end
57
+ @raw = cond
58
+ @compiled = compile_expr(cond, 10)
59
+ end
60
+
61
+ # Evaluate the condition
62
+ # @params *args: BindingAccessor evaluate arguments
63
+ def evaluate(*args)
64
+ evaluate_expr(@compiled, 10, *args)
65
+ end
66
+
67
+ protected
68
+
69
+ def compile_expr(exp, rem)
70
+ return exp if exp == true || exp == false
71
+ raise(Sqreen::Exception, 'too deep call detected') if rem <= 0
72
+ h = {}
73
+ exp.each do |op, values|
74
+ unless op.is_a? String
75
+ raise Sqreen::Exception, "op should be a String (was #{op.class})"
76
+ end
77
+ unless values.is_a?(Array)
78
+ raise Sqreen::Exception, "values should be an Array (was #{values.class})"
79
+ end
80
+ h[op] = values.map do |v|
81
+ case v
82
+ when Hash
83
+ compile_expr(v, rem - 1)
84
+ when 'true'
85
+ true
86
+ when 'false'
87
+ false
88
+ else
89
+ BindingAccessor.new(v.to_s)
90
+ end
91
+ end
92
+ end
93
+ h
94
+ end
95
+
96
+ EQ_OPERATOR = '%equal'.freeze
97
+ NEQ_OPERATOR = '%not_equal'.freeze
98
+ GTE_OPERATOR = '%gte'.freeze
99
+ LTE_OPERATOR = '%lte'.freeze
100
+ GT_OPERATOR = '%gt'.freeze
101
+ LT_OPERATOR = '%lt'.freeze
102
+ HASH_INC_OPERATOR = '%hash_val_include'.freeze
103
+ INC_OPERATOR = '%include'.freeze
104
+ OR_OPERATOR = '%or'.freeze
105
+ AND_OPERATOR = '%and'.freeze
106
+
107
+ OPERATORS_ARITY = {
108
+ HASH_INC_OPERATOR => 3,
109
+ EQ_OPERATOR => 2,
110
+ NEQ_OPERATOR => 2,
111
+ INC_OPERATOR => 2,
112
+ GTE_OPERATOR => 2,
113
+ LTE_OPERATOR => 2,
114
+ GT_OPERATOR => 2,
115
+ LT_OPERATOR => 2,
116
+ }.freeze
117
+
118
+ def evaluate_expr(exp, rem, *args)
119
+ return exp if exp == true || exp == false
120
+ raise(Sqreen::Exception, 'too deep call detected') if rem <= 0
121
+ bool = nil
122
+ exp.each do |op, values|
123
+ res = values.map do |v|
124
+ case v
125
+ when Hash
126
+ evaluate_expr(v, rem - 1, *args)
127
+ when true, false
128
+ v
129
+ else
130
+ v.resolve(*args)
131
+ end
132
+ end
133
+
134
+ arity = OPERATORS_ARITY[op]
135
+ if !arity.nil? && res.size != arity
136
+ raise(Sqreen::Exception, "bad res #{res} (op #{op} wanted #{arity})")
137
+ end
138
+ bool = case op
139
+ when OR_OPERATOR
140
+ res.any?
141
+ when AND_OPERATOR
142
+ res.all?
143
+ when EQ_OPERATOR
144
+ res[0] == res[1]
145
+ when NEQ_OPERATOR
146
+ res[0] != res[1]
147
+ when GT_OPERATOR
148
+ res[0] > res[1]
149
+ when GTE_OPERATOR
150
+ res[0] >= res[1]
151
+ when LT_OPERATOR
152
+ res[0] < res[1]
153
+ when LTE_OPERATOR
154
+ res[0] <= res[1]
155
+ when INC_OPERATOR
156
+ unless res[0].respond_to?(:include?)
157
+ raise(Sqreen::Exception, "no include on res #{res[0].inspect}")
158
+ end
159
+ res[0].include?(res[1])
160
+ when HASH_INC_OPERATOR
161
+ ConditionEvaluator.hash_val_include?(res[0], res[1], res[2])
162
+ else
163
+ # FIXME: this should be check in compile
164
+ raise(Sqreen::Exception, "unknown op #{op})")
165
+ end
166
+ end
167
+ bool
168
+ end
169
+ end
@@ -0,0 +1,50 @@
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/condition_evaluator'
5
+
6
+ module Sqreen
7
+ # A module that will dynamically had preconditions to the pre/post/failing
8
+ # callbacks
9
+ module Conditionable
10
+ # Hook the necessary callback function
11
+ #
12
+ # @param conditions [Hash] hash of callback names => conditions
13
+ def condition_callbacks(conditions)
14
+ base = self.class
15
+ %w(pre post failing).each do |cb|
16
+ next unless conditions.key?(cb)
17
+ conds = conditions[cb]
18
+ next if conds.respond_to?(:empty?) && conds.empty?
19
+ next unless base.method_defined?(cb)
20
+ send("#{cb}_conditions=", ConditionEvaluator.new(conds))
21
+ next if base.instance_variable_get("@conditional_hooked_#{cb}")
22
+ base.send(:alias_method, "#{cb}_without_conditions", cb)
23
+ base.send(:alias_method, cb, "#{cb}_with_conditions")
24
+ base.instance_variable_set("@conditional_hooked_#{cb}", true)
25
+ end
26
+ end
27
+
28
+ def pre_with_conditions(inst, *args, &block)
29
+ eargs = [binding, framework, inst, args, @data, nil]
30
+ return nil if !pre_conditions.nil? && !pre_conditions.evaluate(*eargs)
31
+ pre_without_conditions(inst, *args, &block)
32
+ end
33
+
34
+ def post_with_conditions(rv, inst, *args, &block)
35
+ eargs = [binding, framework, inst, args, @data, rv]
36
+ return nil if !post_conditions.nil? && !post_conditions.evaluate(*eargs)
37
+ post_without_conditions(rv, inst, *args, &block)
38
+ end
39
+
40
+ def failing_with_conditions(rv, inst, *args, &block)
41
+ eargs = [binding, framework, inst, args, @data, rv]
42
+ return nil if !failing_conditions.nil? && !failing_conditions.evaluate(*eargs)
43
+ failing_without_conditions(rv, inst, *args, &block)
44
+ end
45
+
46
+ protected
47
+
48
+ attr_accessor :pre_conditions, :post_conditions, :failing_conditions
49
+ end
50
+ end
@@ -0,0 +1,151 @@
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 'yaml'
5
+ require 'sqreen/performance_notifications/newrelic'
6
+
7
+ module Sqreen
8
+ @config = nil
9
+
10
+ def self::config_init(framework = nil)
11
+ @config = Configuration.new(framework)
12
+ @config.load!
13
+ if @config && config_get(:report_perf_newrelic)
14
+ Sqreen::PerformanceNotifications::NewRelic.enable
15
+ end
16
+ @config
17
+ end
18
+
19
+ def self::config_get(name)
20
+ raise 'No configuration defined' if @config.nil?
21
+ @config.get(name)
22
+ end
23
+
24
+ CONFIG_FILE_BY_ENV = 'SQREEN_CONFIG_FILE'.freeze
25
+
26
+ CONFIG_DESCRIPTION = [
27
+ { :env => :SQREEN_URL, :name => :url,
28
+ :default => 'https://back.sqreen.io' },
29
+ { :env => :SQREEN_TOKEN, :name => :token,
30
+ :default => nil },
31
+ { :env => :SQREEN_RULES, :name => :local_rules,
32
+ :default => nil },
33
+ { :env => :SQREEN_RULES_SIGNATURE, :name => :rules_verify_signature,
34
+ :default => true },
35
+ { :env => :SQREEN_LOG_LEVEL, :name => :log_level,
36
+ :default => 'WARN', :choice => %w(UNKNOWN FATAL ERROR WARN INFO DEBUG) },
37
+ { :env => :SQREEN_LOG_LOCATION, :name => :log_location,
38
+ :default => 'log/sqreen.log' },
39
+ { :env => :SQREEN_RUN_IN_TEST, :name => :run_in_test,
40
+ :default => false },
41
+ { :env => :SQREEN_BLOCK_ALL_RULES, :name => :block_all_rules,
42
+ :default => nil },
43
+ { :env => :SQREEN_REPORT_PERF_NR, :name => :report_perf_newrelic,
44
+ :default => false },
45
+
46
+ ].freeze
47
+
48
+ CONFIG_FILE_NAME = 'sqreen.yml'.freeze
49
+
50
+ # Class to access configurations variables
51
+ # This try to load environment by different ways.
52
+ # 1. By file:
53
+ # a. From path in environment variable SQREEN_CONFIG_FILE
54
+ # b. From path in #{Rails.root}/config/sqreen.yml
55
+ # c. From home in ~/.sqreen.yml
56
+ # 2. From the environment, which overrides whatever result we found in 1.
57
+ class Configuration
58
+ def initialize(framework = nil)
59
+ @framework = framework
60
+ @config = default_values
61
+ end
62
+
63
+ def load!
64
+ path = find_configuration_file
65
+ if path
66
+ file_config = parse_configuration_file(path)
67
+ @config.merge!(file_config)
68
+ end
69
+
70
+ env_config = from_environment
71
+ @config.merge!(env_config)
72
+ end
73
+
74
+ def get(name)
75
+ @config[name.to_sym]
76
+ end
77
+
78
+ def default_values
79
+ res = {}
80
+ Sqreen::CONFIG_DESCRIPTION.each do |param|
81
+ name = param[:name]
82
+ value = param[:default]
83
+ choices = param[:choices]
84
+ if choices && !choices.include?(value)
85
+ msg = format("Invalid value '%s' for env '%s' (allowed: %s)", value, name, choices)
86
+ raise Sqreen::Exception, msg
87
+ end
88
+ res[name] = value
89
+ end
90
+ res
91
+ end
92
+
93
+ def from_environment
94
+ res = {}
95
+ Sqreen::CONFIG_DESCRIPTION.each do |param|
96
+ name = param[:name]
97
+ value = ENV[param[:env].to_s]
98
+ next unless value
99
+ res[name] = value
100
+ end
101
+ res
102
+ end
103
+
104
+ def parse_configuration_file(path)
105
+ res = YAML.load_file path
106
+ return {} unless res.is_a?(Hash)
107
+ if @framework
108
+ env = @framework.framework_infos[:environment]
109
+ res = res[env] if env && res[env].is_a?(Hash)
110
+ end
111
+ # hash keys loaded by YAML are strings instead of symbols
112
+ res.keys.each do |key|
113
+ res[key.to_sym] = res.delete(key)
114
+ end
115
+ res
116
+ end
117
+
118
+ def find_user_home
119
+ homes = %w(HOME HOMEPATH)
120
+ homes.detect { |h| !ENV[h].nil? }
121
+ end
122
+
123
+ def find_configuration_file
124
+ config_file_from_env || local_config_file || config_file_from_home
125
+ end
126
+
127
+ protected
128
+
129
+ def config_file_from_env
130
+ path = ENV[Sqreen::CONFIG_FILE_BY_ENV]
131
+ return path if path && File.exist?(path)
132
+ end
133
+
134
+ def local_config_file
135
+ if @framework && @framework.root
136
+ path = File.join(@framework.root.to_s, 'config', CONFIG_FILE_NAME)
137
+ return path if File.exist?(path)
138
+ else
139
+ path = File.expand_path(File.join('config', 'sqreen.yml'))
140
+ return path if File.exist?(path)
141
+ end
142
+ end
143
+
144
+ def config_file_from_home
145
+ home = find_user_home
146
+ return unless home
147
+ path = File.join(ENV[home], '.' + CONFIG_FILE_NAME)
148
+ return path if File.exist?(path)
149
+ end
150
+ end
151
+ end