sqreen 0.8.11465220943-java → 1.1.01481108064-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.rb +1 -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/sdk.rb +11 -0
- data/lib/sqreen/serializer.rb +46 -0
- data/lib/sqreen/session.rb +22 -12
- data/lib/sqreen/version.rb +1 -1
- metadata +4 -11
- 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: a9d3f7c3fc865a93d78175383a7f424176a288c1
|
4
|
+
data.tar.gz: 26617b49da228995435b7940a0723201895d41f3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: df9fc5626f01080a479cb72ccea334b06d6c04c62a2fa142c34f5f9cb411e484ca333ea0663a79fa8e7109f5f9dff077f1f116631e17f57383433df410f153e6
|
7
|
+
data.tar.gz: 3da6a48af4310d9fdb6f251b85401ef2ae3ab9ba9ca089c0612be02012f59e0b1671e02c0a29740a8f6bba63a0c6485acbf0ff6c37c94030226c639a31d3a581
|
data/lib/sqreen.rb
CHANGED
@@ -11,6 +11,7 @@ require 'sqreen/stats'
|
|
11
11
|
require 'sqreen/exception'
|
12
12
|
require 'sqreen/configuration'
|
13
13
|
require 'sqreen/events/attack'
|
14
|
+
require 'sqreen/sdk'
|
14
15
|
|
15
16
|
require 'thread'
|
16
17
|
|
@@ -26,7 +27,6 @@ Sqreen.framework.on_start do |framework|
|
|
26
27
|
prevent_startup = Sqreen.framework.prevent_startup
|
27
28
|
if !prevent_startup
|
28
29
|
runner = Sqreen::Runner.new(configuration, framework)
|
29
|
-
runner.do_heartbeat
|
30
30
|
runner.run_watcher
|
31
31
|
else
|
32
32
|
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
|