sqreen 0.8.11465220943 → 1.0.0.pre1480953244
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/sqreen.rb +0 -1
- data/lib/sqreen/binding_accessor.rb +61 -49
- data/lib/sqreen/condition_evaluator.rb +17 -12
- data/lib/sqreen/conditionable.rb +2 -1
- data/lib/sqreen/configuration.rb +2 -0
- data/lib/sqreen/deliveries/batch.rb +2 -2
- data/lib/sqreen/events/attack.rb +1 -1
- data/lib/sqreen/frameworks/generic.rb +54 -15
- data/lib/sqreen/frameworks/rails.rb +1 -21
- data/lib/sqreen/frameworks/sinatra.rb +5 -0
- data/lib/sqreen/instrumentation.rb +7 -10
- data/lib/sqreen/log.rb +1 -0
- data/lib/sqreen/metrics/base.rb +4 -0
- data/lib/sqreen/metrics_store.rb +4 -4
- data/lib/sqreen/remote_command.rb +5 -4
- data/lib/sqreen/rules_callbacks.rb +0 -4
- data/lib/sqreen/rules_callbacks/matcher_rule.rb +8 -6
- data/lib/sqreen/rules_callbacks/reflected_xss.rb +55 -11
- data/lib/sqreen/runner.rb +76 -73
- data/lib/sqreen/runtime_infos.rb +3 -2
- data/lib/sqreen/serializer.rb +46 -0
- data/lib/sqreen/session.rb +22 -12
- data/lib/sqreen/version.rb +1 -1
- metadata +6 -14
- data/lib/sqreen/detect.rb +0 -14
- data/lib/sqreen/detect/shell_injection.rb +0 -61
- data/lib/sqreen/detect/sql_injection.rb +0 -115
- data/lib/sqreen/parsers/sql.rb +0 -98
- data/lib/sqreen/parsers/sql_tokenizer.rb +0 -266
- data/lib/sqreen/parsers/unix.rb +0 -110
- data/lib/sqreen/rules_callbacks/shell.rb +0 -33
- data/lib/sqreen/rules_callbacks/sql.rb +0 -41
- data/lib/sqreen/rules_callbacks/system_shell.rb +0 -25
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9c8f5f54c0af763465089699d22bef3c53346dc9
|
4
|
+
data.tar.gz: e0015e87e9c6e62f9859224e7eb47bb193ea04c4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 71533d2617f9a4b06d491a87b394f3da3ec0dd1c6019179d92155433799ddd19d32c3f82249c032ee208239bb252c7ca78aa1fde03bbf6b28f422cbc363400d8
|
7
|
+
data.tar.gz: 607a0981e332784768e172e25721c7edc6f11632f570da2cd955d18e2c5a6059037b14c959fa9a9a3a92100c85751d3325bddaf858027d133cda9aa2aeb1506d
|
data/lib/sqreen.rb
CHANGED
@@ -26,7 +26,6 @@ Sqreen.framework.on_start do |framework|
|
|
26
26
|
prevent_startup = Sqreen.framework.prevent_startup
|
27
27
|
if !prevent_startup
|
28
28
|
runner = Sqreen::Runner.new(configuration, framework)
|
29
|
-
runner.do_heartbeat
|
30
29
|
runner.run_watcher
|
31
30
|
else
|
32
31
|
Sqreen.log.debug("#{prevent_startup} prevented Sqreen startup")
|
@@ -5,7 +5,8 @@ require 'strscan'
|
|
5
5
|
|
6
6
|
# the value located at the given binding
|
7
7
|
class BindingAccessor
|
8
|
-
|
8
|
+
PathElem = Struct.new(:kind, :value)
|
9
|
+
attr_reader :path, :expression
|
9
10
|
|
10
11
|
# Expression to be accessed
|
11
12
|
# @param expression [String] expression to read
|
@@ -30,6 +31,18 @@ class BindingAccessor
|
|
30
31
|
|
31
32
|
protected
|
32
33
|
|
34
|
+
STRING_KIND = 'string'.freeze
|
35
|
+
SYMBOL_KIND = 'symbol'.freeze
|
36
|
+
INTEGER_KIND = 'integer'.freeze
|
37
|
+
LOCAL_VAR_KIND = 'local-variable'.freeze
|
38
|
+
INSTANCE_VAR_KIND = 'instance-variable'.freeze
|
39
|
+
CLASS_VAR_KIND = 'class-variable'.freeze
|
40
|
+
GLOBAL_VAR_KIND = 'global-variable'.freeze
|
41
|
+
CONSTANT_KIND = 'constant'.freeze
|
42
|
+
METHOD_KIND = 'method'.freeze
|
43
|
+
INDEX_KIND = 'index'.freeze
|
44
|
+
SQREEN_VAR_KIND = 'sqreen-variable'.freeze
|
45
|
+
|
33
46
|
if binding.respond_to?(:local_variable_get)
|
34
47
|
def get_local(name, bind)
|
35
48
|
bind.local_variable_get(name)
|
@@ -42,28 +55,28 @@ class BindingAccessor
|
|
42
55
|
|
43
56
|
def resolve_component(current_value, component, binding, env)
|
44
57
|
case component[:kind]
|
45
|
-
when
|
58
|
+
when STRING_KIND, SYMBOL_KIND, INTEGER_KIND
|
46
59
|
component[:value]
|
47
|
-
when
|
48
|
-
get_local(component[:
|
49
|
-
when
|
50
|
-
current_value.instance_variable_get("@#{component[:
|
51
|
-
when
|
52
|
-
current_value.class.class_variable_get("@@#{component[:
|
53
|
-
when
|
54
|
-
instance_eval("$#{component[:
|
55
|
-
when
|
60
|
+
when LOCAL_VAR_KIND
|
61
|
+
get_local(component[:value], binding)
|
62
|
+
when INSTANCE_VAR_KIND
|
63
|
+
current_value.instance_variable_get("@#{component[:value]}")
|
64
|
+
when CLASS_VAR_KIND
|
65
|
+
current_value.class.class_variable_get("@@#{component[:value]}")
|
66
|
+
when GLOBAL_VAR_KIND
|
67
|
+
instance_eval("$#{component[:value]}")
|
68
|
+
when CONSTANT_KIND
|
56
69
|
if current_value
|
57
|
-
current_value.const_get(component[:
|
70
|
+
current_value.const_get(component[:value].to_s)
|
58
71
|
else
|
59
|
-
Object.const_get(component[:
|
72
|
+
Object.const_get(component[:value].to_s)
|
60
73
|
end
|
61
|
-
when
|
62
|
-
current_value.send(component[:
|
63
|
-
when
|
64
|
-
current_value[component[:
|
65
|
-
when
|
66
|
-
resolve_sqreen_variable(component[:
|
74
|
+
when METHOD_KIND
|
75
|
+
current_value.send(component[:value])
|
76
|
+
when INDEX_KIND
|
77
|
+
current_value[component[:value]]
|
78
|
+
when SQREEN_VAR_KIND
|
79
|
+
resolve_sqreen_variable(component[:value], *env)
|
67
80
|
else
|
68
81
|
raise "Do not know how to handle this component #{component.inspect}"
|
69
82
|
end
|
@@ -93,7 +106,7 @@ class BindingAccessor
|
|
93
106
|
@path.push scalar
|
94
107
|
return
|
95
108
|
end
|
96
|
-
if @path.
|
109
|
+
if @path.empty?
|
97
110
|
scan_push_variable
|
98
111
|
else
|
99
112
|
scan_push_method
|
@@ -102,16 +115,17 @@ class BindingAccessor
|
|
102
115
|
scan_push_more_constant if @scan.scan(/\./).nil?
|
103
116
|
raise error_state('Scan stuck') if @scan.pos == pos
|
104
117
|
end
|
118
|
+
ensure
|
119
|
+
@scan = nil
|
105
120
|
end
|
106
121
|
|
107
122
|
def scan_scalar
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
return { :value => @scan[1], :kind => 'string' }
|
123
|
+
if @scan.scan(/\d+/)
|
124
|
+
return PathElem.new(INTEGER_KIND, @scan[0].to_i)
|
125
|
+
elsif @scan.scan(/:(\w+)/)
|
126
|
+
return PathElem.new(SYMBOL_KIND, @scan[1].to_sym)
|
127
|
+
elsif @scan.scan(/'((?:\\.|[^\\'])*)'/)
|
128
|
+
return PathElem.new(STRING_KIND, @scan[1])
|
115
129
|
end
|
116
130
|
end
|
117
131
|
|
@@ -119,11 +133,11 @@ class BindingAccessor
|
|
119
133
|
'[\w\u0080-\u{10ffff}]'
|
120
134
|
else
|
121
135
|
'[\w\x80-\xFF]'
|
122
|
-
|
136
|
+
end
|
123
137
|
|
124
138
|
def scan_push_constant
|
125
139
|
return unless @scan.scan(/([A-Z]#{RUBY_IDENTIFIER_CHAR}+)/)
|
126
|
-
@path <<
|
140
|
+
@path << PathElem.new(CONSTANT_KIND, @scan[1])
|
127
141
|
end
|
128
142
|
|
129
143
|
def scan_push_more_constant
|
@@ -133,30 +147,28 @@ class BindingAccessor
|
|
133
147
|
end
|
134
148
|
|
135
149
|
def scan_push_variable
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
when scan_push_constant
|
150
|
+
if @scan.scan(/\$(#{RUBY_IDENTIFIER_CHAR}+)/)
|
151
|
+
return @path << PathElem.new(GLOBAL_VAR_KIND, @scan[1])
|
152
|
+
elsif @scan.scan(/@@(#{RUBY_IDENTIFIER_CHAR}+)/)
|
153
|
+
return @path << PathElem.new(CLASS_VAR_KIND, @scan[1])
|
154
|
+
elsif @scan.scan(/@(#{RUBY_IDENTIFIER_CHAR}+)/)
|
155
|
+
return @path << PathElem.new(INSTANCE_VAR_KIND, @scan[1])
|
156
|
+
elsif @scan.scan(/#\.(\w+)/)
|
157
|
+
return @path << PathElem.new(SQREEN_VAR_KIND, @scan[1])
|
158
|
+
elsif scan_push_constant
|
146
159
|
return
|
147
|
-
|
148
|
-
return @path <<
|
160
|
+
elsif @scan.scan(/(#{RUBY_IDENTIFIER_CHAR}+)/u)
|
161
|
+
return @path << PathElem.new(LOCAL_VAR_KIND, @scan[1])
|
149
162
|
end
|
150
163
|
end
|
151
164
|
|
152
165
|
def scan_push_method
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
return @path << { :name => @scan[1], :kind => 'method' }
|
166
|
+
if @scan.scan(/@@(#{RUBY_IDENTIFIER_CHAR}+)/)
|
167
|
+
return @path << PathElem.new(CLASS_VAR_KIND, @scan[1])
|
168
|
+
elsif @scan.scan(/@(#{RUBY_IDENTIFIER_CHAR}+)/)
|
169
|
+
return @path << PathElem.new(INSTANCE_VAR_KIND, @scan[1])
|
170
|
+
elsif @scan.scan(/(#{RUBY_IDENTIFIER_CHAR}+)/)
|
171
|
+
return @path << PathElem.new(METHOD_KIND, @scan[1])
|
160
172
|
end
|
161
173
|
end
|
162
174
|
|
@@ -165,7 +177,7 @@ class BindingAccessor
|
|
165
177
|
scalar = scan_scalar
|
166
178
|
raise error_state('Invalid index') unless scalar
|
167
179
|
raise error_state('Unfinished index') unless @scan.scan(/\]/)
|
168
|
-
@path <<
|
180
|
+
@path << PathElem.new(INDEX_KIND, scalar[:value])
|
169
181
|
end
|
170
182
|
end
|
171
183
|
|
@@ -30,11 +30,15 @@ class ConditionEvaluator
|
|
30
30
|
# @params hash [Hash] Hash to search into
|
31
31
|
# @params min_value_size [Fixnum] to compare against
|
32
32
|
def self.hash_val_include?(value, hash, min_value_size, rem = 10)
|
33
|
-
return
|
34
|
-
|
33
|
+
return true if rem <= 0
|
34
|
+
vals = hash
|
35
|
+
vals = hash.values if hash.is_a?(Hash)
|
36
|
+
|
37
|
+
vals.any? do |hval|
|
35
38
|
case hval
|
36
|
-
when Hash
|
37
|
-
ConditionEvaluator.hash_val_include?(value, hval,
|
39
|
+
when Hash, Array
|
40
|
+
ConditionEvaluator.hash_val_include?(value, hval,
|
41
|
+
min_value_size, rem - 1)
|
38
42
|
when NilClass
|
39
43
|
false
|
40
44
|
else
|
@@ -42,11 +46,11 @@ class ConditionEvaluator
|
|
42
46
|
false
|
43
47
|
else
|
44
48
|
v = hval.to_s
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
49
|
+
if v.size < min_value_size
|
50
|
+
false
|
51
|
+
else
|
52
|
+
ConditionEvaluator.str_include?(value.to_s, v)
|
53
|
+
end
|
50
54
|
end
|
51
55
|
end
|
52
56
|
end
|
@@ -91,6 +95,7 @@ class ConditionEvaluator
|
|
91
95
|
|
92
96
|
def compile_expr(exp, rem)
|
93
97
|
return exp if exp == true || exp == false
|
98
|
+
return true if exp.empty?
|
94
99
|
raise(Sqreen::Exception, 'too deep call detected') if rem <= 0
|
95
100
|
h = {}
|
96
101
|
exp.each do |op, values|
|
@@ -140,9 +145,9 @@ class ConditionEvaluator
|
|
140
145
|
|
141
146
|
def evaluate_expr(exp, rem, *args)
|
142
147
|
return exp if exp == true || exp == false
|
148
|
+
return true if exp.empty?
|
143
149
|
raise(Sqreen::Exception, 'too deep call detected') if rem <= 0
|
144
|
-
|
145
|
-
exp.each do |op, values|
|
150
|
+
exp.all? do |op, values|
|
146
151
|
res = values.map do |v|
|
147
152
|
case v
|
148
153
|
when Hash
|
@@ -190,7 +195,7 @@ class ConditionEvaluator
|
|
190
195
|
# FIXME: this should be check in compile
|
191
196
|
raise(Sqreen::Exception, "unknown op #{op})")
|
192
197
|
end
|
198
|
+
bool
|
193
199
|
end
|
194
|
-
bool
|
195
200
|
end
|
196
201
|
end
|
data/lib/sqreen/conditionable.rb
CHANGED
@@ -18,7 +18,8 @@ module Sqreen
|
|
18
18
|
next if conds.respond_to?(:empty?) && conds.empty?
|
19
19
|
next unless base.method_defined?(cb)
|
20
20
|
send("#{cb}_conditions=", ConditionEvaluator.new(conds))
|
21
|
-
|
21
|
+
defd = base.instance_variable_defined?("@conditional_hooked_#{cb}")
|
22
|
+
next if defd && base.instance_variable_get("@conditional_hooked_#{cb}")
|
22
23
|
base.send(:alias_method, "#{cb}_without_conditions", cb)
|
23
24
|
base.send(:alias_method, cb, "#{cb}_with_conditions")
|
24
25
|
base.instance_variable_set("@conditional_hooked_#{cb}", true)
|
data/lib/sqreen/configuration.rb
CHANGED
data/lib/sqreen/events/attack.rb
CHANGED
@@ -9,6 +9,15 @@ module Sqreen
|
|
9
9
|
class GenericFramework
|
10
10
|
attr_accessor :sqreen_configuration
|
11
11
|
|
12
|
+
def initialize
|
13
|
+
if defined?(Rack::Builder)
|
14
|
+
hook_rack_builder
|
15
|
+
else
|
16
|
+
to_app_done(Process.pid)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
|
12
21
|
# What kind of database is this
|
13
22
|
def db_settings(_options = {})
|
14
23
|
raise Sqreen::NotImplementedYet
|
@@ -97,21 +106,23 @@ module Sqreen
|
|
97
106
|
|
98
107
|
# Instrument with our rules when the framework as finished loading
|
99
108
|
def instrument_when_ready!(instrumentor, rules)
|
100
|
-
|
101
|
-
|
102
|
-
unless defined?(Rack::Builder)
|
103
|
-
done = true
|
104
|
-
return
|
109
|
+
wait_for_to_app do
|
110
|
+
instrumentor.instrument!(rules, self)
|
105
111
|
end
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
112
|
+
end
|
113
|
+
|
114
|
+
def to_app_done(val)
|
115
|
+
return if @to_app_done
|
116
|
+
@to_app_done = val
|
117
|
+
return unless @wait
|
118
|
+
@wait.each(&:call)
|
119
|
+
@wait.clear
|
120
|
+
end
|
121
|
+
|
122
|
+
def wait_for_to_app(&block)
|
123
|
+
yield && return if @to_app_done
|
124
|
+
@wait ||= []
|
125
|
+
@wait << block
|
115
126
|
end
|
116
127
|
|
117
128
|
# Does the parameters include this value
|
@@ -217,6 +228,33 @@ module Sqreen
|
|
217
228
|
|
218
229
|
protected
|
219
230
|
|
231
|
+
def hook_rack_request(klass)
|
232
|
+
@calling_pid = Process.pid
|
233
|
+
klass.class_eval do
|
234
|
+
define_method(:call_with_sqreen) do |*args, &block|
|
235
|
+
rv = call_without_sqreen(*args, &block)
|
236
|
+
if Sqreen.framework.instance_variable_get('@calling_pid') != Process.pid
|
237
|
+
Sqreen.framework.instance_variable_set('@calling_pid', Process.pid)
|
238
|
+
yield Sqreen.framework
|
239
|
+
end
|
240
|
+
rv
|
241
|
+
end
|
242
|
+
alias_method :call_without_sqreen, :call
|
243
|
+
alias_method :call, :call_with_sqreen
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
def hook_rack_builder
|
248
|
+
Rack::Builder.class_eval do
|
249
|
+
define_method(:to_app_with_sqreen) do |*args, &block|
|
250
|
+
Sqreen.framework.to_app_done(Process.pid)
|
251
|
+
to_app_without_sqreen(*args, &block)
|
252
|
+
end
|
253
|
+
alias_method :to_app_without_sqreen, :to_app
|
254
|
+
alias_method :to_app, :to_app_with_sqreen
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
220
258
|
# FIXME: Extract to another object (utils?)
|
221
259
|
# FIXME: protect against cycles ?
|
222
260
|
def each_value_for_hash(params, &block)
|
@@ -229,11 +267,12 @@ module Sqreen
|
|
229
267
|
end
|
230
268
|
|
231
269
|
def ensure_rack_loaded
|
270
|
+
@cannot_load_rack ||= false
|
232
271
|
return false if @cannot_load_rack
|
233
272
|
require 'rack' unless defined?(Rack)
|
234
273
|
true
|
235
274
|
rescue LoadError => e
|
236
|
-
# FIXME find a nice way to test this branch
|
275
|
+
# FIXME: find a nice way to test this branch
|
237
276
|
Sqreen::RemoteException.record(e)
|
238
277
|
@cannot_load_rack = true
|
239
278
|
false
|
@@ -73,30 +73,10 @@ module Sqreen
|
|
73
73
|
end
|
74
74
|
end
|
75
75
|
|
76
|
-
def hook_rack_request(app)
|
77
|
-
saved_meth_name = :call_without_sqreen_hooked
|
78
|
-
new_method = :call_with_sqreen_hooked
|
79
|
-
|
80
|
-
app.class.class_eval do
|
81
|
-
alias_method saved_meth_name, :call
|
82
|
-
|
83
|
-
define_method(new_method) do |*args, &cblock|
|
84
|
-
rv = send(saved_meth_name, *args, &cblock)
|
85
|
-
if Sqreen.framework.instance_variable_get('@calling_pid') != Process.pid
|
86
|
-
Sqreen.framework.instance_variable_set('@calling_pid', Process.pid)
|
87
|
-
yield Sqreen.framework
|
88
|
-
end
|
89
|
-
rv
|
90
|
-
end
|
91
|
-
|
92
|
-
alias_method :call, new_method
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
76
|
def on_start(&block)
|
97
77
|
@calling_pid = Process.pid
|
98
78
|
Init.startup do |app|
|
99
|
-
hook_rack_request(app, &block)
|
79
|
+
hook_rack_request(app.class, &block)
|
100
80
|
app.config.after_initialize do
|
101
81
|
yield self
|
102
82
|
end
|