sqreen 1.5.0-java → 1.6.0-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 +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
|