sqreen 1.4.2-java → 1.4.3-java
Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3be83ec63339da677df7f8d12efd3f57c5ae135b
|
4
|
+
data.tar.gz: 873ab18040fdb66fd7a6f4ebe75d6c465b2035e0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 208b06ca1100546c07a8512d5ab22ee9630f749408aa7f450ba7ea41c7449c952f16f58ef76c3d2631183f64f353bb53fc94d52a6c871f18cfd7cbcea7b64530
|
7
|
+
data.tar.gz: 503bfca4f48c619745153cbfa9049cdc0fbb234a320dfdab74a7a0672604b50b0c36d8dd0540af1c53dc3433c6b611204b8da39c6487709c3c45825ea131ae72
|
@@ -56,6 +56,34 @@ class ConditionEvaluator
|
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
59
|
+
# Predicate: Is one of values deeply present in keys of hash
|
60
|
+
# @params value [Array] Array of objects to find
|
61
|
+
# @params hash [Hash] Hash to search into
|
62
|
+
# @params min_value_size [Fixnum] to compare against
|
63
|
+
def self.hash_key_include?(values, hash, min_value_size, rem = 10)
|
64
|
+
return true if rem <= 0
|
65
|
+
if hash.is_a?(Array)
|
66
|
+
return hash.any? do |v|
|
67
|
+
ConditionEvaluator.hash_key_include?(values, v, min_value_size, rem - 1)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
return false unless hash.is_a?(Hash)
|
72
|
+
|
73
|
+
hash.any? do |hkey, hval|
|
74
|
+
case hkey
|
75
|
+
when NilClass
|
76
|
+
false
|
77
|
+
else
|
78
|
+
if hkey.respond_to?(:empty?) && hkey.empty?
|
79
|
+
false
|
80
|
+
else
|
81
|
+
values.include?(hkey.to_s) || ConditionEvaluator.hash_key_include?(values, hval, min_value_size, rem - 1)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
59
87
|
# Test is a str contains what. Rencode if necessary
|
60
88
|
def self.str_include?(str, what)
|
61
89
|
str1 = if str.encoding != Encoding::UTF_8
|
@@ -128,12 +156,14 @@ class ConditionEvaluator
|
|
128
156
|
GT_OPERATOR = '%gt'.freeze
|
129
157
|
LT_OPERATOR = '%lt'.freeze
|
130
158
|
HASH_INC_OPERATOR = '%hash_val_include'.freeze
|
159
|
+
HASH_KEY_OPERATOR = '%hash_key_include'.freeze
|
131
160
|
INC_OPERATOR = '%include'.freeze
|
132
161
|
OR_OPERATOR = '%or'.freeze
|
133
162
|
AND_OPERATOR = '%and'.freeze
|
134
163
|
|
135
164
|
OPERATORS_ARITY = {
|
136
165
|
HASH_INC_OPERATOR => 3,
|
166
|
+
HASH_KEY_OPERATOR => 3,
|
137
167
|
EQ_OPERATOR => 2,
|
138
168
|
NEQ_OPERATOR => 2,
|
139
169
|
INC_OPERATOR => 2,
|
@@ -191,6 +221,8 @@ class ConditionEvaluator
|
|
191
221
|
end
|
192
222
|
when HASH_INC_OPERATOR
|
193
223
|
ConditionEvaluator.hash_val_include?(res[0], res[1], res[2])
|
224
|
+
when HASH_KEY_OPERATOR
|
225
|
+
ConditionEvaluator.hash_key_include?(res[0], res[1], res[2])
|
194
226
|
else
|
195
227
|
# FIXME: this should be check in compile
|
196
228
|
raise(Sqreen::Exception, "unknown op #{op})")
|
@@ -167,7 +167,7 @@ module Sqreen
|
|
167
167
|
@wait << block
|
168
168
|
end
|
169
169
|
|
170
|
-
# Does the parameters include this value
|
170
|
+
# Does the parameters value include this value
|
171
171
|
def params_include?(value)
|
172
172
|
params = request_params
|
173
173
|
return false if params.nil?
|
@@ -177,6 +177,16 @@ module Sqreen
|
|
177
177
|
false
|
178
178
|
end
|
179
179
|
|
180
|
+
# Does the parameters key/value include this value
|
181
|
+
def full_params_include?(value)
|
182
|
+
params = request_params
|
183
|
+
return false if params.nil?
|
184
|
+
each_key_value_for_hash(params) do |param|
|
185
|
+
return true if param == value
|
186
|
+
end
|
187
|
+
false
|
188
|
+
end
|
189
|
+
|
180
190
|
# Fetch and store the current request object
|
181
191
|
# Nota: cleanup should be performed at end of request (see clean_request)
|
182
192
|
def store_request(object)
|
@@ -313,6 +323,18 @@ module Sqreen
|
|
313
323
|
end
|
314
324
|
end
|
315
325
|
|
326
|
+
def each_key_value_for_hash(params, &block)
|
327
|
+
case params
|
328
|
+
when Hash then params.each do |k, v|
|
329
|
+
yield k
|
330
|
+
each_key_value_for_hash(v, &block)
|
331
|
+
end
|
332
|
+
when Array then params.each { |v| each_key_value_for_hash(v, &block) }
|
333
|
+
else
|
334
|
+
yield params
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
316
338
|
def ensure_rack_loaded
|
317
339
|
@cannot_load_rack ||= false
|
318
340
|
return false if @cannot_load_rack
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# Copyright (c) 2015 Sqreen. All Rights Reserved.
|
2
2
|
# Please refer to our terms for more information: https://www.sqreen.io/terms.html
|
3
3
|
|
4
|
+
require 'sqreen/rule_attributes'
|
4
5
|
require 'sqreen/rule_callback'
|
5
6
|
|
6
7
|
module Sqreen
|
@@ -14,5 +15,25 @@ module Sqreen
|
|
14
15
|
advise_action(nil)
|
15
16
|
end
|
16
17
|
end
|
18
|
+
|
19
|
+
# Count 1 for each things located by the binding accessor
|
20
|
+
class BindingAccessorCounter < RuleCB
|
21
|
+
def initialize(klass, method, rule_hash)
|
22
|
+
super(klass, method, rule_hash)
|
23
|
+
@accessors = @data['values'].map do |expr|
|
24
|
+
BindingAccessor.new(expr, true)
|
25
|
+
end
|
26
|
+
@metric_category = rule_hash[Attrs::METRICS].first['name']
|
27
|
+
end
|
28
|
+
|
29
|
+
def post(rv, inst, *args, &_block)
|
30
|
+
return unless rv.is_a?(Array) && !rv.empty?
|
31
|
+
key = @accessors.map do |accessor|
|
32
|
+
accessor.resolve(binding, framework, inst, args, @data, rv)
|
33
|
+
end
|
34
|
+
record_observation(@metric_category, JSON.dump(key), 1)
|
35
|
+
advise_action(nil)
|
36
|
+
end
|
37
|
+
end
|
17
38
|
end
|
18
39
|
end
|
@@ -10,8 +10,24 @@ require 'sqreen/rules_callbacks/regexp_rule'
|
|
10
10
|
module Sqreen
|
11
11
|
# Sqreen rules
|
12
12
|
module Rules
|
13
|
+
# XSSCB abstract common behaviour of tpls
|
14
|
+
class XSSCB < RegexpRuleCB
|
15
|
+
# The remaining code is only to find out if user entry was an attack,
|
16
|
+
# and record it. Since we don't rely on it to respond to user, it would
|
17
|
+
# be better to do it in background.
|
18
|
+
def report_dangerous_xss(value)
|
19
|
+
found = match_regexp(value)
|
20
|
+
|
21
|
+
return unless found
|
22
|
+
infos = {
|
23
|
+
:found => found,
|
24
|
+
:payload => value,
|
25
|
+
}
|
26
|
+
record_event(infos)
|
27
|
+
end
|
28
|
+
end
|
13
29
|
# look for reflected XSS with erb template engine
|
14
|
-
class ReflectedXSSCB <
|
30
|
+
class ReflectedXSSCB < XSSCB
|
15
31
|
def pre(_inst, *args, &_block)
|
16
32
|
value = args[0]
|
17
33
|
|
@@ -38,33 +54,20 @@ module Sqreen
|
|
38
54
|
advise_action(nil)
|
39
55
|
end
|
40
56
|
|
41
|
-
# The remaining code is only to find out if user entry was an attack,
|
42
|
-
# and record it. Since we don't rely on it to respond to user, it would
|
43
|
-
# be better to do it in background.
|
44
|
-
def report_dangerous_xss(value)
|
45
|
-
found = match_regexp(value)
|
46
|
-
|
47
|
-
return unless found
|
48
|
-
infos = {
|
49
|
-
:found => found,
|
50
|
-
:payload => value,
|
51
|
-
}
|
52
|
-
record_event(infos)
|
53
|
-
end
|
54
57
|
end
|
55
58
|
# look for reflected XSS with haml template engine
|
56
59
|
# hook function arguments of
|
57
60
|
# Haml::Buffer.format_script(result, preserve_script, in_tag, preserve_tag,
|
58
61
|
# escape_html, nuke_inner_whitespace,
|
59
62
|
# interpolated, ugly)
|
60
|
-
class ReflectedXSSHamlCB <
|
63
|
+
class ReflectedXSSHamlCB < XSSCB
|
61
64
|
def post(ret, _inst, *_args, &_block)
|
62
65
|
value = ret
|
63
66
|
return if value.nil?
|
64
67
|
|
65
68
|
# Sqreen::log.debug value
|
66
69
|
|
67
|
-
return unless framework.
|
70
|
+
return unless framework.full_params_include?(value)
|
68
71
|
|
69
72
|
Sqreen.log.debug { format('Found unescaped user param: %s', value) }
|
70
73
|
|
@@ -103,10 +106,117 @@ module Sqreen
|
|
103
106
|
nil
|
104
107
|
end
|
105
108
|
end
|
109
|
+
|
110
|
+
class Haml4UtilInterpolationHookCB < RuleCB
|
111
|
+
def pre(_inst, *args, &_block)
|
112
|
+
str = args[0]
|
113
|
+
escape_html = args[1]
|
114
|
+
# Original code from HAML tuned up to insert escape_haml call
|
115
|
+
res = ''
|
116
|
+
rest = Haml::Util.handle_interpolation str.dump do |scan|
|
117
|
+
escapes = (scan[2].size - 1) / 2
|
118
|
+
res << scan.matched[0...-3 - escapes]
|
119
|
+
if escapes.odd?
|
120
|
+
res << '#{'
|
121
|
+
else
|
122
|
+
content = eval('"' + Haml::Util.balance(scan, '{', '}', 1)[0][0...-1] + '"')
|
123
|
+
content = "Haml::Helpers.html_escape((#{content}))" if escape_html
|
124
|
+
res << '#{Sqreen.escape_haml(' + content + ')}' # Use eval to get rid of string escapes
|
125
|
+
end
|
126
|
+
end
|
127
|
+
{ :status => :skip, :new_return_value => res + rest }
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# Hook build attributes
|
132
|
+
class Haml4CompilerBuildAttributeCB < XSSCB
|
133
|
+
def pre(inst, *args, &_block)
|
134
|
+
attrs = args[-1]
|
135
|
+
new_attrs, found_xss = Haml4CompilerBuildAttributeCB.clean_hash_key(attrs) do |key|
|
136
|
+
if !key.nil? && key.is_a?(String) && framework.full_params_include?(key)
|
137
|
+
Sqreen.log.debug { format('Found unescaped user param: %s', key) }
|
138
|
+
report_dangerous_xss(key)
|
139
|
+
[CGI.escape_html(key), true]
|
140
|
+
else
|
141
|
+
[key, false]
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
return if !found_xss || !block
|
146
|
+
# potential XSS! let's escape
|
147
|
+
if !framework || !find_whitelisted_path(framework.request_path.to_s)
|
148
|
+
args[-1] = new_attrs
|
149
|
+
r = inst.send(method, *args)
|
150
|
+
return { :status => :skip, :new_return_value => r }
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def self.clean_hash_key(hash, limit = 10, seen = [], &block)
|
155
|
+
seen << hash.object_id
|
156
|
+
has_xss = false
|
157
|
+
new_h = {}
|
158
|
+
return if limit <= 0
|
159
|
+
hash.each do |k, v|
|
160
|
+
if seen.include?(v.object_id)
|
161
|
+
new_h[k] = nil
|
162
|
+
next
|
163
|
+
end
|
164
|
+
seen << v.object_id
|
165
|
+
new_key, found_xss = yield k
|
166
|
+
has_xss |= found_xss
|
167
|
+
if v.is_a?(Hash)
|
168
|
+
new_h[new_key], found_xss = Haml4CompilerBuildAttributeCB.clean_hash_key(v, limit - 1, seen, &block)
|
169
|
+
has_xss |= found_xss
|
170
|
+
else
|
171
|
+
new_h[new_key] = v
|
172
|
+
end
|
173
|
+
end
|
174
|
+
[new_h, has_xss]
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# Hook into temple template rendering
|
179
|
+
class TempleEscapableHookCB < RuleCB
|
180
|
+
def post(ret, _inst, *_args, &_block)
|
181
|
+
ret[1] = "Sqreen.escape_temple(#{ret[1]})"
|
182
|
+
{ :status => :override, :new_return_value => ret }
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
# Hook into temple template rendering
|
187
|
+
class SlimSplatBuilderCB < XSSCB
|
188
|
+
def pre(inst, *args, &_block)
|
189
|
+
value = args[0]
|
190
|
+
return if value.nil?
|
191
|
+
|
192
|
+
return unless framework.full_params_include?(value)
|
193
|
+
|
194
|
+
Sqreen.log.debug { format('Found unescaped user param: %s', value) }
|
195
|
+
|
196
|
+
return unless value.is_a?(String)
|
197
|
+
|
198
|
+
report_dangerous_xss(value)
|
199
|
+
|
200
|
+
return unless block
|
201
|
+
# potential XSS! let's escape
|
202
|
+
if block &&
|
203
|
+
(!framework || !find_whitelisted_path(framework.request_path.to_s))
|
204
|
+
args[0] = CGI.escape_html(value)
|
205
|
+
r = inst.send(method, *args)
|
206
|
+
return { :status => :skip, :new_return_value => r }
|
207
|
+
end
|
208
|
+
nil
|
209
|
+
end
|
210
|
+
end
|
106
211
|
end
|
107
212
|
|
108
213
|
# Escape HAML when instrumented to do it
|
109
214
|
def self.escape_haml(x)
|
110
215
|
x
|
111
216
|
end
|
217
|
+
|
218
|
+
# Escape Temple when instrumented to do it
|
219
|
+
def self.escape_temple(x)
|
220
|
+
x
|
221
|
+
end
|
112
222
|
end
|
data/lib/sqreen/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sqreen
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.4.
|
4
|
+
version: 1.4.3
|
5
5
|
platform: java
|
6
6
|
authors:
|
7
7
|
- Sqreen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-04-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: execjs
|