sqreen 1.5.0-java → 1.6.0-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/sqreen/attack_detected.html +2 -0
- data/lib/sqreen/binding_accessor.rb +247 -157
- data/lib/sqreen/condition_evaluator.rb +191 -189
- data/lib/sqreen/frameworks/generic.rb +6 -0
- data/lib/sqreen/frameworks/rails.rb +1 -0
- data/lib/sqreen/frameworks/sinatra.rb +18 -0
- data/lib/sqreen/middleware.rb +10 -0
- data/lib/sqreen/rules_callbacks.rb +3 -0
- data/lib/sqreen/rules_callbacks/binding_accessor_matcher.rb +75 -0
- data/lib/sqreen/rules_callbacks/custom_error.rb +57 -0
- data/lib/sqreen/rules_callbacks/execjs.rb +4 -7
- data/lib/sqreen/rules_callbacks/matcher_rule.rb +26 -20
- data/lib/sqreen/version.rb +1 -1
- metadata +6 -3
@@ -4,230 +4,232 @@
|
|
4
4
|
require 'sqreen/binding_accessor'
|
5
5
|
require 'sqreen/exception'
|
6
6
|
|
7
|
-
|
8
|
-
#
|
9
|
-
#
|
10
|
-
# { "%
|
11
|
-
# { "%
|
12
|
-
#
|
13
|
-
#
|
14
|
-
# { "%
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
# { "%hash_val_include" => ["
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
7
|
+
module Sqreen
|
8
|
+
# Evaluate a condition, resolving literals using BindingAccessor.
|
9
|
+
#
|
10
|
+
# { "%and" => ["true", "true"] } -> true
|
11
|
+
# { "%or" => ["true", "false"] } -> true
|
12
|
+
# { "%and" => ["false", "true"] } -> false
|
13
|
+
#
|
14
|
+
# { "%equal" => ["coucou", "#.args[0]"] } -> "coucou" == args[0]
|
15
|
+
# { "%hash_val_include" => ["toto is a small guy", "#.request_params"] } ->
|
16
|
+
# true if one value of request params in included
|
17
|
+
# in the sentence 'toto is a small guy'.
|
18
|
+
#
|
19
|
+
# Combine expressions:
|
20
|
+
# { "%or" =>
|
21
|
+
# [
|
22
|
+
# { "%hash_val_include" => ["AAA", "#.request_params"] },
|
23
|
+
# { "%hash_val_include" => ["BBB", "#.request_params"] },
|
24
|
+
# ]
|
25
|
+
# }
|
26
|
+
# will return true if one of the request_params include either AAA or BBB.
|
27
|
+
#
|
28
|
+
class ConditionEvaluator
|
29
|
+
# Predicate: Is value deeply included in hash
|
30
|
+
# @params value [Object] object to find
|
31
|
+
# @params hash [Hash] Hash to search into
|
32
|
+
# @params min_value_size [Fixnum] to compare against
|
33
|
+
def self.hash_val_include?(value, hash, min_value_size, rem = 10)
|
34
|
+
return true if rem <= 0
|
35
|
+
vals = hash
|
36
|
+
vals = hash.values if hash.is_a?(Hash)
|
36
37
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
false
|
44
|
-
else
|
45
|
-
if hval.respond_to?(:empty?) && hval.empty?
|
38
|
+
vals.any? do |hval|
|
39
|
+
case hval
|
40
|
+
when Hash, Array
|
41
|
+
ConditionEvaluator.hash_val_include?(value, hval,
|
42
|
+
min_value_size, rem - 1)
|
43
|
+
when NilClass
|
46
44
|
false
|
47
45
|
else
|
48
|
-
|
49
|
-
if v.size < min_value_size
|
46
|
+
if hval.respond_to?(:empty?) && hval.empty?
|
50
47
|
false
|
51
48
|
else
|
52
|
-
|
49
|
+
v = hval.to_s
|
50
|
+
if v.size < min_value_size
|
51
|
+
false
|
52
|
+
else
|
53
|
+
ConditionEvaluator.str_include?(value.to_s, v)
|
54
|
+
end
|
53
55
|
end
|
54
56
|
end
|
55
57
|
end
|
56
58
|
end
|
57
|
-
end
|
58
59
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
60
|
+
# Predicate: Is one of values deeply present in keys of hash
|
61
|
+
# @params value [Array] Array of objects to find
|
62
|
+
# @params hash [Hash] Hash to search into
|
63
|
+
# @params min_value_size [Fixnum] to compare against
|
64
|
+
def self.hash_key_include?(values, hash, min_value_size, rem = 10)
|
65
|
+
return true if rem <= 0
|
66
|
+
if hash.is_a?(Array)
|
67
|
+
return hash.any? do |v|
|
68
|
+
ConditionEvaluator.hash_key_include?(values, v, min_value_size, rem - 1)
|
69
|
+
end
|
68
70
|
end
|
69
|
-
end
|
70
71
|
|
71
|
-
|
72
|
+
return false unless hash.is_a?(Hash)
|
72
73
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
false
|
77
|
-
else
|
78
|
-
if hkey.respond_to?(:empty?) && hkey.empty?
|
74
|
+
hash.any? do |hkey, hval|
|
75
|
+
case hkey
|
76
|
+
when NilClass
|
79
77
|
false
|
80
78
|
else
|
81
|
-
|
79
|
+
if hkey.respond_to?(:empty?) && hkey.empty?
|
80
|
+
false
|
81
|
+
else
|
82
|
+
values.include?(hkey.to_s) || ConditionEvaluator.hash_key_include?(values, hval, min_value_size, rem - 1)
|
83
|
+
end
|
82
84
|
end
|
83
85
|
end
|
84
86
|
end
|
85
|
-
end
|
86
87
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
88
|
+
# Test is a str contains what. Rencode if necessary
|
89
|
+
def self.str_include?(str, what)
|
90
|
+
str1 = if str.encoding != Encoding::UTF_8
|
91
|
+
str.encode(Encoding::UTF_8, :invalid => :replace,
|
92
|
+
:undef => :replace)
|
93
|
+
else
|
94
|
+
str
|
95
|
+
end
|
96
|
+
str2 = if what.encoding != Encoding::UTF_8
|
97
|
+
what.encode(Encoding::UTF_8, :invalid => :replace,
|
98
|
+
:undef => :replace)
|
99
|
+
else
|
100
|
+
what
|
101
|
+
end
|
102
|
+
str1.include?(str2)
|
103
|
+
end
|
103
104
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
105
|
+
# Initialize evaluator
|
106
|
+
# @param cond [Hash] condition Hash
|
107
|
+
def initialize(cond)
|
108
|
+
unless cond == true || cond == false
|
109
|
+
unless cond.respond_to? :each
|
110
|
+
raise(Sqreen::Exception, "cond should be a Hash (was #{cond.class})")
|
111
|
+
end
|
110
112
|
end
|
113
|
+
@raw = cond
|
114
|
+
@compiled = compile_expr(cond, 10)
|
111
115
|
end
|
112
|
-
@raw = cond
|
113
|
-
@compiled = compile_expr(cond, 10)
|
114
|
-
end
|
115
116
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
117
|
+
# Evaluate the condition
|
118
|
+
# @params *args: BindingAccessor evaluate arguments
|
119
|
+
def evaluate(*args)
|
120
|
+
evaluate_expr(@compiled, 10, *args)
|
121
|
+
end
|
121
122
|
|
122
|
-
|
123
|
+
protected
|
123
124
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
125
|
+
def compile_expr(exp, rem)
|
126
|
+
return exp if exp == true || exp == false
|
127
|
+
return true if exp.empty?
|
128
|
+
raise(Sqreen::Exception, 'too deep call detected') if rem <= 0
|
129
|
+
h = {}
|
130
|
+
exp.each do |op, values|
|
131
|
+
unless op.is_a? String
|
132
|
+
raise Sqreen::Exception, "op should be a String (was #{op.class})"
|
133
|
+
end
|
134
|
+
unless values.is_a?(Array)
|
135
|
+
raise Sqreen::Exception, "values should be an Array (was #{values.class})"
|
136
|
+
end
|
137
|
+
h[op] = values.map do |v|
|
138
|
+
case v
|
139
|
+
when Hash
|
140
|
+
compile_expr(v, rem - 1)
|
141
|
+
when 'true'
|
142
|
+
true
|
143
|
+
when 'false'
|
144
|
+
false
|
145
|
+
else
|
146
|
+
BindingAccessor.new(v.to_s)
|
147
|
+
end
|
146
148
|
end
|
147
149
|
end
|
150
|
+
h
|
148
151
|
end
|
149
|
-
h
|
150
|
-
end
|
151
152
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
153
|
+
EQ_OPERATOR = '%equal'.freeze
|
154
|
+
NEQ_OPERATOR = '%not_equal'.freeze
|
155
|
+
GTE_OPERATOR = '%gte'.freeze
|
156
|
+
LTE_OPERATOR = '%lte'.freeze
|
157
|
+
GT_OPERATOR = '%gt'.freeze
|
158
|
+
LT_OPERATOR = '%lt'.freeze
|
159
|
+
HASH_INC_OPERATOR = '%hash_val_include'.freeze
|
160
|
+
HASH_KEY_OPERATOR = '%hash_key_include'.freeze
|
161
|
+
INC_OPERATOR = '%include'.freeze
|
162
|
+
OR_OPERATOR = '%or'.freeze
|
163
|
+
AND_OPERATOR = '%and'.freeze
|
163
164
|
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
165
|
+
OPERATORS_ARITY = {
|
166
|
+
HASH_INC_OPERATOR => 3,
|
167
|
+
HASH_KEY_OPERATOR => 3,
|
168
|
+
EQ_OPERATOR => 2,
|
169
|
+
NEQ_OPERATOR => 2,
|
170
|
+
INC_OPERATOR => 2,
|
171
|
+
GTE_OPERATOR => 2,
|
172
|
+
LTE_OPERATOR => 2,
|
173
|
+
GT_OPERATOR => 2,
|
174
|
+
LT_OPERATOR => 2,
|
175
|
+
}.freeze
|
175
176
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
177
|
+
def evaluate_expr(exp, rem, *args)
|
178
|
+
return exp if exp == true || exp == false
|
179
|
+
return true if exp.empty?
|
180
|
+
raise(Sqreen::Exception, 'too deep call detected') if rem <= 0
|
181
|
+
exp.all? do |op, values|
|
182
|
+
res = values.map do |v|
|
183
|
+
case v
|
184
|
+
when Hash
|
185
|
+
evaluate_expr(v, rem - 1, *args)
|
186
|
+
when true, false
|
187
|
+
v
|
188
|
+
else
|
189
|
+
v.resolve(*args)
|
190
|
+
end
|
189
191
|
end
|
190
|
-
end
|
191
192
|
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
193
|
+
arity = OPERATORS_ARITY[op]
|
194
|
+
if !arity.nil? && res.size != arity
|
195
|
+
raise(Sqreen::Exception, "bad res #{res} (op #{op} wanted #{arity})")
|
196
|
+
end
|
197
|
+
bool = case op
|
198
|
+
when OR_OPERATOR
|
199
|
+
res.any?
|
200
|
+
when AND_OPERATOR
|
201
|
+
res.all?
|
202
|
+
when EQ_OPERATOR
|
203
|
+
res[0] == res[1]
|
204
|
+
when NEQ_OPERATOR
|
205
|
+
res[0] != res[1]
|
206
|
+
when GT_OPERATOR
|
207
|
+
res[0] > res[1]
|
208
|
+
when GTE_OPERATOR
|
209
|
+
res[0] >= res[1]
|
210
|
+
when LT_OPERATOR
|
211
|
+
res[0] < res[1]
|
212
|
+
when LTE_OPERATOR
|
213
|
+
res[0] <= res[1]
|
214
|
+
when INC_OPERATOR
|
215
|
+
unless res[0].respond_to?(:include?)
|
216
|
+
raise(Sqreen::Exception, "no include on res #{res[0].inspect}")
|
217
|
+
end
|
218
|
+
if res[0].is_a?(String)
|
219
|
+
ConditionEvaluator.str_include?(res[0], res[1])
|
220
|
+
else
|
221
|
+
res[0].include?(res[1])
|
222
|
+
end
|
223
|
+
when HASH_INC_OPERATOR
|
224
|
+
ConditionEvaluator.hash_val_include?(res[0], res[1], res[2])
|
225
|
+
when HASH_KEY_OPERATOR
|
226
|
+
ConditionEvaluator.hash_key_include?(res[0], res[1], res[2])
|
219
227
|
else
|
220
|
-
|
228
|
+
# FIXME: this should be check in compile
|
229
|
+
raise(Sqreen::Exception, "unknown op #{op})")
|
221
230
|
end
|
222
|
-
|
223
|
-
|
224
|
-
when HASH_KEY_OPERATOR
|
225
|
-
ConditionEvaluator.hash_key_include?(res[0], res[1], res[2])
|
226
|
-
else
|
227
|
-
# FIXME: this should be check in compile
|
228
|
-
raise(Sqreen::Exception, "unknown op #{op})")
|
229
|
-
end
|
230
|
-
bool
|
231
|
+
bool
|
232
|
+
end
|
231
233
|
end
|
232
234
|
end
|
233
235
|
end
|
@@ -79,6 +79,7 @@ module Sqreen
|
|
79
79
|
def self.startup
|
80
80
|
initializer 'sqreen.startup' do |app|
|
81
81
|
app.middleware.insert_before(Rack::Runtime, Sqreen::Middleware)
|
82
|
+
app.middleware.insert_after(ActionDispatch::DebugExceptions, Sqreen::ErrorHandlingMiddleware)
|
82
83
|
yield app
|
83
84
|
end
|
84
85
|
end
|
@@ -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/frameworks/generic'
|
5
|
+
require 'sqreen/middleware'
|
5
6
|
|
6
7
|
module Sqreen
|
7
8
|
module Frameworks
|
@@ -15,6 +16,7 @@ module Sqreen
|
|
15
16
|
end
|
16
17
|
|
17
18
|
def on_start(&block)
|
19
|
+
hook_app_build(Sinatra::Base)
|
18
20
|
hook_rack_request(Sinatra::Application, &block)
|
19
21
|
yield self
|
20
22
|
end
|
@@ -34,6 +36,22 @@ module Sqreen
|
|
34
36
|
db_infos = { :name => adapter_name }
|
35
37
|
[db_type, db_infos]
|
36
38
|
end
|
39
|
+
|
40
|
+
def hook_app_build(klass)
|
41
|
+
klass.singleton_class.class_eval do
|
42
|
+
define_method(:setup_default_middleware_with_sqreen) do |builder|
|
43
|
+
ret = setup_default_middleware_without_sqreen(builder)
|
44
|
+
builder.instance_variable_get('@use').insert(2, proc do |app|
|
45
|
+
# Inject error middle just before sinatra one
|
46
|
+
Sqreen::ErrorHandlingMiddleware.new(app)
|
47
|
+
end)
|
48
|
+
ret
|
49
|
+
end
|
50
|
+
|
51
|
+
alias_method :setup_default_middleware_without_sqreen, :setup_default_middleware
|
52
|
+
alias_method :setup_default_middleware, :setup_default_middleware_with_sqreen
|
53
|
+
end
|
54
|
+
end
|
37
55
|
end
|
38
56
|
end
|
39
57
|
end
|