sqreen 0.7.01461158029-java
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.
- checksums.yaml +7 -0
- data/CODE_OF_CONDUCT.md +22 -0
- data/README.md +77 -0
- data/Rakefile +40 -0
- data/lib/sqreen.rb +67 -0
- data/lib/sqreen/binding_accessor.rb +184 -0
- data/lib/sqreen/ca.crt +72 -0
- data/lib/sqreen/callback_tree.rb +78 -0
- data/lib/sqreen/callbacks.rb +120 -0
- data/lib/sqreen/capped_queue.rb +23 -0
- data/lib/sqreen/condition_evaluator.rb +169 -0
- data/lib/sqreen/conditionable.rb +50 -0
- data/lib/sqreen/configuration.rb +151 -0
- data/lib/sqreen/context.rb +22 -0
- data/lib/sqreen/deliveries/batch.rb +80 -0
- data/lib/sqreen/deliveries/simple.rb +36 -0
- data/lib/sqreen/detect.rb +14 -0
- data/lib/sqreen/detect/shell_injection.rb +61 -0
- data/lib/sqreen/detect/sql_injection.rb +115 -0
- data/lib/sqreen/event.rb +16 -0
- data/lib/sqreen/events/attack.rb +60 -0
- data/lib/sqreen/events/remote_exception.rb +53 -0
- data/lib/sqreen/exception.rb +31 -0
- data/lib/sqreen/frameworks.rb +40 -0
- data/lib/sqreen/frameworks/generic.rb +243 -0
- data/lib/sqreen/frameworks/rails.rb +155 -0
- data/lib/sqreen/frameworks/rails3.rb +36 -0
- data/lib/sqreen/frameworks/sinatra.rb +34 -0
- data/lib/sqreen/frameworks/sqreen_test.rb +26 -0
- data/lib/sqreen/instrumentation.rb +504 -0
- data/lib/sqreen/log.rb +116 -0
- data/lib/sqreen/metrics.rb +6 -0
- data/lib/sqreen/metrics/average.rb +39 -0
- data/lib/sqreen/metrics/base.rb +41 -0
- data/lib/sqreen/metrics/collect.rb +22 -0
- data/lib/sqreen/metrics/sum.rb +20 -0
- data/lib/sqreen/metrics_store.rb +94 -0
- data/lib/sqreen/parsers/sql.rb +98 -0
- data/lib/sqreen/parsers/sql_tokenizer.rb +266 -0
- data/lib/sqreen/parsers/unix.rb +110 -0
- data/lib/sqreen/payload_creator.rb +132 -0
- data/lib/sqreen/performance_notifications.rb +86 -0
- data/lib/sqreen/performance_notifications/log.rb +36 -0
- data/lib/sqreen/performance_notifications/metrics.rb +36 -0
- data/lib/sqreen/performance_notifications/newrelic.rb +36 -0
- data/lib/sqreen/remote_command.rb +82 -0
- data/lib/sqreen/rule_attributes.rb +25 -0
- data/lib/sqreen/rule_callback.rb +97 -0
- data/lib/sqreen/rules.rb +116 -0
- data/lib/sqreen/rules_callbacks.rb +29 -0
- data/lib/sqreen/rules_callbacks/binding_accessor_metrics.rb +79 -0
- data/lib/sqreen/rules_callbacks/count_http_codes.rb +18 -0
- data/lib/sqreen/rules_callbacks/crawler_user_agent_matches.rb +24 -0
- data/lib/sqreen/rules_callbacks/crawler_user_agent_matches_metrics.rb +25 -0
- data/lib/sqreen/rules_callbacks/execjs.rb +136 -0
- data/lib/sqreen/rules_callbacks/headers_insert.rb +20 -0
- data/lib/sqreen/rules_callbacks/inspect_rule.rb +20 -0
- data/lib/sqreen/rules_callbacks/matcher_rule.rb +103 -0
- data/lib/sqreen/rules_callbacks/rails_parameters.rb +14 -0
- data/lib/sqreen/rules_callbacks/record_request_context.rb +23 -0
- data/lib/sqreen/rules_callbacks/reflected_xss.rb +40 -0
- data/lib/sqreen/rules_callbacks/regexp_rule.rb +36 -0
- data/lib/sqreen/rules_callbacks/shell.rb +33 -0
- data/lib/sqreen/rules_callbacks/shell_env.rb +32 -0
- data/lib/sqreen/rules_callbacks/sql.rb +41 -0
- data/lib/sqreen/rules_callbacks/system_shell.rb +25 -0
- data/lib/sqreen/rules_callbacks/url_matches.rb +25 -0
- data/lib/sqreen/rules_callbacks/user_agent_matches.rb +22 -0
- data/lib/sqreen/rules_signature.rb +142 -0
- data/lib/sqreen/runner.rb +312 -0
- data/lib/sqreen/runtime_infos.rb +127 -0
- data/lib/sqreen/session.rb +340 -0
- data/lib/sqreen/stats.rb +18 -0
- data/lib/sqreen/version.rb +6 -0
- 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
|