shikashi 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/AUTHORS +4 -0
- data/CHANGELOG +5 -1
- data/README +33 -33
- data/Rakefile +3 -2
- data/examples/basic/example.rb +3 -8
- data/examples/basic/example2.rb +1 -1
- data/examples/basic/example3.rb +0 -1
- data/examples/basic/example4.rb +5 -10
- data/examples/basic/example5.rb +5 -7
- data/examples/basic/example7.rb +0 -2
- data/examples/basic/example8.rb +33 -0
- data/lib/shikashi/privileges.rb +19 -35
- data/lib/shikashi/sandbox.rb +92 -169
- metadata +22 -8
- data/examples/redir/example1.rb +0 -26
- data/examples/redir/example2.rb +0 -40
- data/test/functional/test_timeout.rb +0 -36
data/AUTHORS
CHANGED
data/CHANGELOG
CHANGED
@@ -1,4 +1,8 @@
|
|
1
|
-
0.2.0
|
1
|
+
0.2.0 shikashi HookHandler opened to allow use it in nested hook handlers
|
2
|
+
|
3
|
+
Added restriction to avoid the system calls when using %x[] and ``
|
4
|
+
|
5
|
+
Added control over the access to constants and global variables (See documentation)
|
2
6
|
|
3
7
|
Remove rallhook dependency, replaced by getsource
|
4
8
|
|
data/README
CHANGED
@@ -138,9 +138,6 @@ define a class from inside the sandbox and use it from outside
|
|
138
138
|
# allow execution of print
|
139
139
|
priv.allow_method :print
|
140
140
|
|
141
|
-
# allow definition of classes
|
142
|
-
priv.allow_class_definitions
|
143
|
-
|
144
141
|
#inside the sandbox, only can use method foo on main and method times on instances of Fixnum
|
145
142
|
s.run(priv, '
|
146
143
|
class X
|
@@ -154,48 +151,51 @@ define a class from inside the sandbox and use it from outside
|
|
154
151
|
end
|
155
152
|
')
|
156
153
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
x.foo
|
161
|
-
begin
|
154
|
+
x = s.base_namespace::X.new
|
155
|
+
x.foo
|
156
|
+
begin
|
162
157
|
x.bar
|
163
|
-
|
164
|
-
|
165
|
-
end
|
158
|
+
rescue SecurityError => e
|
159
|
+
print "x.bar failed due security errors: #{e}\n"
|
166
160
|
end
|
161
|
+
|
167
162
|
|
168
|
-
|
169
|
-
=== Redirection feature example
|
170
|
-
|
171
|
-
Simple redirection of method, the method foo is replaced by TestWrapper::call
|
163
|
+
=== Base namespace
|
172
164
|
|
173
165
|
require "rubygems"
|
174
166
|
require "shikashi"
|
175
167
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
original_call(*args)
|
180
|
-
end
|
168
|
+
include Shikashi
|
169
|
+
|
170
|
+
module SandboxModule
|
181
171
|
end
|
182
172
|
|
183
173
|
class X
|
184
|
-
|
185
|
-
|
186
|
-
|
174
|
+
def foo
|
175
|
+
print "X#foo\n"
|
176
|
+
end
|
187
177
|
end
|
188
178
|
|
189
|
-
s =
|
190
|
-
|
179
|
+
s = Sandbox.new
|
180
|
+
priv = Privileges.new
|
181
|
+
priv.allow_method :print
|
191
182
|
|
192
|
-
|
193
|
-
|
183
|
+
s.run( "
|
184
|
+
class ::X
|
185
|
+
def foo
|
186
|
+
print \"foo defined inside the sandbox\\n\"
|
187
|
+
end
|
188
|
+
end
|
189
|
+
", priv, :base_namespace => SandboxModule)
|
190
|
+
|
191
|
+
|
192
|
+
x = X.new # X class is not affected by the sandbox (The X Class defined in the sandbox is SandboxModule::X)
|
193
|
+
x.foo
|
194
|
+
|
195
|
+
x = SandboxModule::X.new
|
196
|
+
x.foo
|
194
197
|
|
195
|
-
# redirect calls to foo to TestWrapper
|
196
|
-
perm.instances_of(X).redirect :foo, TestWrapper
|
197
198
|
|
198
|
-
s.run(perm,"X.new.foo")
|
199
199
|
|
200
200
|
=== Timeout example
|
201
201
|
|
@@ -208,8 +208,8 @@ Simple redirection of method, the method foo is replaced by TestWrapper::call
|
|
208
208
|
perm.allow_method :sleep
|
209
209
|
|
210
210
|
s.run(perm,"sleep 3", :timeout => 2) # raise Shikashi::Timeout::Error after 2 seconds
|
211
|
-
|
212
|
-
|
211
|
+
|
212
|
+
|
213
213
|
== Copying
|
214
214
|
|
215
|
-
Copyright (c) 2010 Dario Seminara, released under the GPL License (see LICENSE)
|
215
|
+
Copyright (c) 2010-2011 Dario Seminara, released under the GPL License (see LICENSE)
|
data/Rakefile
CHANGED
@@ -6,14 +6,15 @@ require 'rake/gempackagetask'
|
|
6
6
|
|
7
7
|
spec = Gem::Specification.new do |s|
|
8
8
|
s.name = 'shikashi'
|
9
|
-
s.version = '0.
|
9
|
+
s.version = '0.3.0'
|
10
10
|
s.author = 'Dario Seminara'
|
11
11
|
s.email = 'robertodarioseminara@gmail.com'
|
12
12
|
s.platform = Gem::Platform::RUBY
|
13
|
-
s.summary = 'shikashi is a ruby sandbox that permits the execution of "unprivileged" scripts by defining the permitted methods and constants the scripts can invoke
|
13
|
+
s.summary = 'shikashi is a ruby sandbox that permits the execution of "unprivileged" scripts by defining the permitted methods and constants the scripts can invoke with a white list logic'
|
14
14
|
s.homepage = "http://github.com/tario/shikashi"
|
15
15
|
s.add_dependency "evalhook", ">= 0.1.0"
|
16
16
|
s.add_dependency "getsource", ">= 0.1.0"
|
17
|
+
s.add_dependency "evalmimic", ">= 0.1.0"
|
17
18
|
s.has_rdoc = true
|
18
19
|
s.extra_rdoc_files = [ 'README' ]
|
19
20
|
# s.rdoc_options << '--main' << 'README'
|
data/examples/basic/example.rb
CHANGED
@@ -7,8 +7,6 @@ include Shikashi
|
|
7
7
|
s = Sandbox.new
|
8
8
|
priv = Privileges.new
|
9
9
|
|
10
|
-
priv.allow_method :singleton_method_added
|
11
|
-
|
12
10
|
# allow execution of foo in this object
|
13
11
|
priv.object(self).allow :foo
|
14
12
|
|
@@ -17,7 +15,7 @@ priv.object(self).allow :print
|
|
17
15
|
|
18
16
|
#inside the sandbox, only can use method foo on main and method times on instances of Fixnum
|
19
17
|
code = "
|
20
|
-
def
|
18
|
+
def inside_foo(a)
|
21
19
|
print 'inside_foo'
|
22
20
|
if (a)
|
23
21
|
system('ls -l') # denied
|
@@ -25,10 +23,7 @@ def self.inside_foo(a)
|
|
25
23
|
end
|
26
24
|
"
|
27
25
|
|
28
|
-
s.run(priv,
|
26
|
+
s.run(code, priv, :no_base_namespace => true)
|
29
27
|
|
30
|
-
s.run do
|
31
28
|
inside_foo(false)
|
32
|
-
inside_foo(true) #SecurityError
|
33
|
-
|
34
|
-
end
|
29
|
+
#inside_foo(true) #SecurityError
|
data/examples/basic/example2.rb
CHANGED
@@ -20,5 +20,5 @@ priv.object(self).allow :foo
|
|
20
20
|
priv.instances_of(Fixnum).allow :times
|
21
21
|
|
22
22
|
#inside the sandbox, only can use method foo on main and method times on instances of Fixnum
|
23
|
-
s.run(priv, "2.times do foo end")
|
23
|
+
s.run(priv, "2.times do foo end", :no_base_namespace => true)
|
24
24
|
|
data/examples/basic/example3.rb
CHANGED
@@ -31,7 +31,6 @@ priv.object(X).allow :new
|
|
31
31
|
# allow instance methods of X. Note that the method privileged_operations is not allowed
|
32
32
|
priv.instances_of(X).allow :foo, :bar
|
33
33
|
|
34
|
-
priv.allow_method :=== # for exception handling
|
35
34
|
#inside the sandbox, only can use method foo on main and method times on instances of Fixnum
|
36
35
|
s.run(priv, '
|
37
36
|
x = X.new
|
data/examples/basic/example4.rb
CHANGED
@@ -11,9 +11,6 @@ priv = Privileges.new
|
|
11
11
|
# allow execution of print
|
12
12
|
priv.allow_method :print
|
13
13
|
|
14
|
-
# allow singleton methods
|
15
|
-
priv.allow_singleton_methods
|
16
|
-
|
17
14
|
#inside the sandbox, only can use method foo on main and method times on instances of Fixnum
|
18
15
|
s.run(priv, '
|
19
16
|
module A
|
@@ -27,12 +24,10 @@ end
|
|
27
24
|
')
|
28
25
|
|
29
26
|
# run privileged code in the sandbox, if not, the methods defined in the sandbox are invisible from outside
|
30
|
-
s.
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
print "A.inside_foo(true) failed due security errors: #{e}\n"
|
36
|
-
end
|
27
|
+
s.base_namespace::A.inside_foo(false)
|
28
|
+
begin
|
29
|
+
s.base_namespace::A.inside_foo(true)
|
30
|
+
rescue SecurityError => e
|
31
|
+
print "A.inside_foo(true) failed due security errors: #{e}\n"
|
37
32
|
end
|
38
33
|
|
data/examples/basic/example5.rb
CHANGED
@@ -28,13 +28,11 @@ end
|
|
28
28
|
')
|
29
29
|
|
30
30
|
# run privileged code in the sandbox, if not, the methods defined in the sandbox are invisible from outside
|
31
|
-
s.
|
32
|
-
|
33
|
-
|
34
|
-
begin
|
31
|
+
x = s.base_namespace::X.new
|
32
|
+
x.foo
|
33
|
+
begin
|
35
34
|
x.bar
|
36
|
-
|
37
|
-
|
38
|
-
end
|
35
|
+
rescue SecurityError => e
|
36
|
+
print "x.bar failed due security errors: #{e}\n"
|
39
37
|
end
|
40
38
|
|
data/examples/basic/example7.rb
CHANGED
@@ -0,0 +1,33 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "shikashi"
|
3
|
+
|
4
|
+
include Shikashi
|
5
|
+
|
6
|
+
module SandboxModule
|
7
|
+
end
|
8
|
+
|
9
|
+
class X
|
10
|
+
def foo
|
11
|
+
print "X#foo\n"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
s = Sandbox.new
|
16
|
+
priv = Privileges.new
|
17
|
+
priv.allow_method :print
|
18
|
+
|
19
|
+
s.run( "
|
20
|
+
class ::X
|
21
|
+
def foo
|
22
|
+
print \"foo defined inside the sandbox\\n\"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
", priv, :base_namespace => SandboxModule)
|
26
|
+
|
27
|
+
|
28
|
+
x = X.new # X class is not affected by the sandbox (The X Class defined in the sandbox is SandboxModule::X)
|
29
|
+
x.foo
|
30
|
+
|
31
|
+
x = SandboxModule::X.new
|
32
|
+
x.foo
|
33
|
+
|
data/lib/shikashi/privileges.rb
CHANGED
@@ -85,26 +85,6 @@ public
|
|
85
85
|
@all = true
|
86
86
|
end
|
87
87
|
|
88
|
-
def redirect(method_name, method_wrapper_class)
|
89
|
-
allow method_name
|
90
|
-
@redirect_hash[method_name] = method_wrapper_class
|
91
|
-
end
|
92
|
-
|
93
|
-
def handle_redirection(klass, recv, method_name, sandbox)
|
94
|
-
rclass = @redirect_hash[method_name.to_sym]
|
95
|
-
|
96
|
-
if rclass
|
97
|
-
if block_given?
|
98
|
-
rclass.redirect_handler(klass, recv, method_name, 0, sandbox) do |mh|
|
99
|
-
yield(mh)
|
100
|
-
end
|
101
|
-
else
|
102
|
-
rclass.redirect_handler(klass, recv, method_name, 0, sandbox)
|
103
|
-
end
|
104
|
-
else
|
105
|
-
nil
|
106
|
-
end
|
107
|
-
end
|
108
88
|
end
|
109
89
|
|
110
90
|
def initialize
|
@@ -191,21 +171,6 @@ public
|
|
191
171
|
@allowed_methods << method_name
|
192
172
|
end
|
193
173
|
|
194
|
-
def handle_redirection(klass, recv, method_name, sandbox)
|
195
|
-
if @last_allowed
|
196
|
-
if block_given?
|
197
|
-
@last_allowed.handle_redirection(klass, recv, method_name, sandbox) do |mh|
|
198
|
-
yield(mh)
|
199
|
-
end
|
200
|
-
else
|
201
|
-
@last_allowed.handle_redirection(klass, recv, method_name, sandbox)
|
202
|
-
end
|
203
|
-
else
|
204
|
-
nil
|
205
|
-
end
|
206
|
-
|
207
|
-
end
|
208
|
-
|
209
174
|
def allow?(klass, recv, method_name, method_id)
|
210
175
|
|
211
176
|
m = nil
|
@@ -285,6 +250,9 @@ public
|
|
285
250
|
end
|
286
251
|
end
|
287
252
|
|
253
|
+
def xstr_allowed?
|
254
|
+
@xstr_allowed
|
255
|
+
end
|
288
256
|
|
289
257
|
def global_allowed?(varname)
|
290
258
|
@allowed_globals.include? varname
|
@@ -294,6 +262,22 @@ public
|
|
294
262
|
@allowed_consts.include? varname
|
295
263
|
end
|
296
264
|
|
265
|
+
# defines the permissions needed to execute system calls from the script
|
266
|
+
#
|
267
|
+
# Example:
|
268
|
+
#
|
269
|
+
# s = Sandbox.new
|
270
|
+
# priv = Privileges.new
|
271
|
+
#
|
272
|
+
# priv.allow_xstr
|
273
|
+
#
|
274
|
+
# s.run(priv, '
|
275
|
+
# %x[ls -l]
|
276
|
+
# ')
|
277
|
+
def allow_xstr
|
278
|
+
@xstr_allowed = true
|
279
|
+
end
|
280
|
+
|
297
281
|
# defines the permissions needed to create or change a global variable
|
298
282
|
#
|
299
283
|
# Example:
|
data/lib/shikashi/sandbox.rb
CHANGED
@@ -24,6 +24,7 @@ require "shikashi/privileges"
|
|
24
24
|
require "shikashi/pick_argument"
|
25
25
|
require "getsource"
|
26
26
|
require "timeout"
|
27
|
+
require "evalmimic"
|
27
28
|
|
28
29
|
module Shikashi
|
29
30
|
|
@@ -67,6 +68,8 @@ module Shikashi
|
|
67
68
|
#Binding of execution, the default is a binding in a global context allowing the definition of module of classes
|
68
69
|
attr_reader :chain
|
69
70
|
|
71
|
+
attr_reader :hook_handler
|
72
|
+
|
70
73
|
#
|
71
74
|
# Generate a random source file name for the sandbox, used internally
|
72
75
|
#
|
@@ -78,7 +81,6 @@ module Shikashi
|
|
78
81
|
def initialize
|
79
82
|
@privileges = Hash.new
|
80
83
|
@chain = Hash.new
|
81
|
-
@redirect_hash = Hash.new
|
82
84
|
end
|
83
85
|
|
84
86
|
# add a chain of sources, used internally
|
@@ -86,138 +88,28 @@ module Shikashi
|
|
86
88
|
@chain[inner] = outer
|
87
89
|
end
|
88
90
|
|
91
|
+
def base_namespace
|
92
|
+
@base_namespace
|
93
|
+
end
|
89
94
|
|
90
|
-
|
91
|
-
#
|
92
|
-
#= Example 1
|
93
|
-
#Basic redirection
|
94
|
-
#
|
95
|
-
# require "rubygems"
|
96
|
-
# require "shikashi"
|
97
|
-
#
|
98
|
-
# class TestWrapper < Shikashi::Sandbox::MethodWrapper
|
99
|
-
# def call(*args)
|
100
|
-
# print "called foo from source: #{source}, arguments: #{args.inspect} \n"
|
101
|
-
# original_call(*args)
|
102
|
-
# end
|
103
|
-
# end
|
104
|
-
#
|
105
|
-
#
|
106
|
-
# class X
|
107
|
-
# def foo
|
108
|
-
# print "original foo\n"
|
109
|
-
# end
|
110
|
-
# end
|
111
|
-
#
|
112
|
-
#
|
113
|
-
# s = Shikashi::Sandbox.new
|
114
|
-
# perm = Shikashi::Privileges.new
|
115
|
-
#
|
116
|
-
# perm.object(X).allow :new
|
117
|
-
# perm.instances_of(X).allow :foo
|
118
|
-
#
|
119
|
-
# # redirect calls to foo to TestWrapper
|
120
|
-
# perm.instances_of(X).redirect :foo, TestWrapper
|
121
|
-
#
|
122
|
-
# s.run(perm,"X.new.foo")
|
123
|
-
#
|
124
|
-
#= Example 2
|
125
|
-
#Proper block handling on redirection wrapper
|
126
|
-
#
|
127
|
-
# require "rubygems"
|
128
|
-
# require "shikashi"
|
129
|
-
#
|
130
|
-
# class TestWrapper < Shikashi::Sandbox::MethodWrapper
|
131
|
-
# def call(*args)
|
132
|
-
# print "called #{klass}#each block_given?:#{block_given?}, source: #{source}\n"
|
133
|
-
# if block_given?
|
134
|
-
# original_call(*args) do |*x|
|
135
|
-
# print "yielded value #{x.first}\n"
|
136
|
-
# yield(*x)
|
137
|
-
# end
|
138
|
-
# else
|
139
|
-
# original_call(*args)
|
140
|
-
# end
|
141
|
-
# end
|
142
|
-
# end
|
143
|
-
#
|
144
|
-
# s = Shikashi::Sandbox.new
|
145
|
-
# perm = Shikashi::Privileges.new
|
146
|
-
#
|
147
|
-
# perm.instances_of(Array).allow :each
|
148
|
-
# perm.instances_of(Array).redirect :each, TestWrapper
|
149
|
-
#
|
150
|
-
# perm.instances_of(Enumerable::Enumerator).allow :each
|
151
|
-
# perm.instances_of(Enumerable::Enumerator).redirect :each, TestWrapper
|
152
|
-
#
|
153
|
-
# perm.allow_method :print
|
154
|
-
#
|
155
|
-
# s.run perm, '
|
156
|
-
# array = [1,2,3]
|
157
|
-
#
|
158
|
-
# array.each do |x|
|
159
|
-
# print x,"\n"
|
160
|
-
# end
|
161
|
-
#
|
162
|
-
# enum = array.each
|
163
|
-
# enum.each do |x|
|
164
|
-
# print x,"\n"
|
165
|
-
# end
|
166
|
-
# '
|
167
|
-
class MethodWrapper
|
168
|
-
|
169
|
-
class MethodRedirect
|
170
|
-
include RedirectHelper::MethodRedirect
|
171
|
-
|
172
|
-
attr_accessor :klass
|
173
|
-
attr_accessor :method_name
|
174
|
-
attr_accessor :recv
|
175
|
-
end
|
176
|
-
|
177
|
-
attr_accessor :recv
|
178
|
-
attr_accessor :method_name
|
179
|
-
attr_accessor :klass
|
95
|
+
class EvalhookHandler < EvalHook::HookHandler
|
180
96
|
attr_accessor :sandbox
|
181
|
-
attr_accessor :privileges
|
182
|
-
attr_accessor :source
|
183
|
-
|
184
|
-
def self.redirect_handler(klass,recv,method_name,method_id,sandbox)
|
185
|
-
mw = self.new
|
186
|
-
mw.klass = klass
|
187
|
-
mw.recv = recv
|
188
|
-
mw.method_name = method_name
|
189
|
-
mw.sandbox = sandbox
|
190
|
-
|
191
|
-
if block_given?
|
192
|
-
yield(mw)
|
193
|
-
end
|
194
97
|
|
195
|
-
|
98
|
+
def handle_xstr( str )
|
99
|
+
source = get_caller
|
196
100
|
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
mr
|
202
|
-
end
|
203
|
-
|
204
|
-
def original_call(*args)
|
205
|
-
if block_given?
|
206
|
-
klass.instance_method(method_name).bind(recv).call(*args) do |*x|
|
207
|
-
yield(*x)
|
101
|
+
privileges = sandbox.privileges[source]
|
102
|
+
if privileges
|
103
|
+
unless privileges.xstr_allowed?
|
104
|
+
raise SecurityError, "fobidden shell commands"
|
208
105
|
end
|
209
|
-
else
|
210
|
-
klass.instance_method(method_name).bind(recv).call(*args)
|
211
106
|
end
|
212
|
-
end
|
213
|
-
end
|
214
107
|
|
215
|
-
|
216
|
-
|
217
|
-
attr_accessor :redirect
|
108
|
+
`#{str}`
|
109
|
+
end
|
218
110
|
|
219
111
|
def handle_gasgn( global_id, value )
|
220
|
-
source =
|
112
|
+
source = get_caller
|
221
113
|
|
222
114
|
privileges = sandbox.privileges[source]
|
223
115
|
if privileges
|
@@ -230,7 +122,7 @@ module Shikashi
|
|
230
122
|
end
|
231
123
|
|
232
124
|
def handle_cdecl(klass, const_id, value)
|
233
|
-
source =
|
125
|
+
source = get_caller
|
234
126
|
|
235
127
|
privileges = sandbox.privileges[source]
|
236
128
|
if privileges
|
@@ -240,8 +132,6 @@ module Shikashi
|
|
240
132
|
end
|
241
133
|
|
242
134
|
nil
|
243
|
-
|
244
|
-
|
245
135
|
end
|
246
136
|
|
247
137
|
def handle_method(klass, recv, method_name)
|
@@ -251,15 +141,17 @@ module Shikashi
|
|
251
141
|
|
252
142
|
if method_name
|
253
143
|
|
254
|
-
source =
|
144
|
+
source = self.get_caller
|
255
145
|
dest_source = klass.instance_method(method_name).body.file
|
256
146
|
|
257
147
|
privileges = nil
|
258
148
|
if source != dest_source then
|
259
149
|
privileges = sandbox.privileges[source]
|
260
150
|
|
261
|
-
|
262
|
-
|
151
|
+
unless privileges then
|
152
|
+
raise SecurityError.new("Cannot invoke method #{method_name} on object of class #{klass}")
|
153
|
+
else
|
154
|
+
# privileges = privileges.dup
|
263
155
|
loop_source = source
|
264
156
|
loop_privileges = privileges
|
265
157
|
|
@@ -284,23 +176,16 @@ module Shikashi
|
|
284
176
|
return nil if method_name == :instance_eval
|
285
177
|
return nil if method_name == :binding
|
286
178
|
|
287
|
-
|
288
|
-
wclass = @redirect[method_name.to_sym]
|
289
|
-
if wclass then
|
290
|
-
return wclass.redirect_handler(klass,recv,method_name,method_id,sandbox)
|
291
|
-
end
|
292
|
-
end
|
179
|
+
nil
|
293
180
|
|
294
181
|
end
|
295
182
|
|
296
|
-
if privileges
|
297
|
-
privileges.handle_redirection(klass,recv,method_name,sandbox) do |mh|
|
298
|
-
mh.privileges = privileges
|
299
|
-
mh.source = source
|
300
|
-
end
|
301
|
-
end
|
302
183
|
|
303
184
|
end # if
|
185
|
+
|
186
|
+
def get_caller
|
187
|
+
caller[2].split(":").first
|
188
|
+
end
|
304
189
|
end # Class
|
305
190
|
|
306
191
|
#Run the code in sandbox with the given privileges, also run privileged code in the sandbox context for
|
@@ -369,12 +254,53 @@ module Shikashi
|
|
369
254
|
# end
|
370
255
|
#
|
371
256
|
#
|
372
|
-
|
373
257
|
def run(*args)
|
258
|
+
end
|
259
|
+
|
260
|
+
define_eval_method :run
|
261
|
+
def internal_eval(b_, args)
|
262
|
+
|
263
|
+
newargs = Array.new
|
264
|
+
|
265
|
+
timeout = args.pick(:timeout) do nil end
|
266
|
+
privileges_ = args.pick(Privileges,:privileges) do Privileges.new end
|
267
|
+
code = args.pick(String,:code)
|
268
|
+
binding_ = args.pick(Binding,:binding) do b_ end
|
269
|
+
source = args.pick(:source) do nil end
|
270
|
+
base_namespace = args.pick(:base_namespace) do create_adhoc_base_namespace end
|
271
|
+
@base_namespace = base_namespace
|
272
|
+
no_base_namespace = args.pick(:no_base_namespace) do false end
|
273
|
+
|
274
|
+
run_i(code, privileges_, binding_, :base_namespace => base_namespace, :timeout => timeout, :no_base_namespace => no_base_namespace)
|
275
|
+
end
|
276
|
+
|
277
|
+
def create_hook_handler(*args)
|
278
|
+
hook_handler = EvalhookHandler.new
|
279
|
+
hook_handler.sandbox = self
|
280
|
+
@base_namespace = args.pick(:base_namespace) do create_adhoc_base_namespace end
|
281
|
+
hook_handler.base_namespace = @base_namespace
|
282
|
+
|
283
|
+
source = args.pick(:source) do generate_id end
|
284
|
+
privileges_ = args.pick(Privileges,:privileges) do Privileges.new end
|
285
|
+
|
286
|
+
self.privileges[source] = privileges_
|
287
|
+
|
288
|
+
hook_handler
|
289
|
+
end
|
290
|
+
|
291
|
+
|
292
|
+
private
|
293
|
+
|
294
|
+
def create_adhoc_base_namespace
|
295
|
+
rnd_module_name = "SandboxBasenamespace#{rand(100000000)}"
|
296
|
+
|
297
|
+
eval("module Shikashi::Sandbox::#{rnd_module_name}; end")
|
298
|
+
@base_namespace = eval("Shikashi::Sandbox::#{rnd_module_name}")
|
299
|
+
@base_namespace
|
300
|
+
end
|
301
|
+
|
302
|
+
def run_i(*args)
|
374
303
|
|
375
|
-
handler = EvalhookHandler.new
|
376
|
-
handler.redirect = @redirect_hash
|
377
|
-
handler.sandbox = self
|
378
304
|
|
379
305
|
t = args.pick(:timeout) do nil end
|
380
306
|
raise Shikashi::Timeout::Error if t == 0
|
@@ -389,10 +315,26 @@ module Shikashi
|
|
389
315
|
code = args.pick(String,:code)
|
390
316
|
binding_ = args.pick(Binding,:binding) do Shikashi.global_binding end
|
391
317
|
source = args.pick(:source) do generate_id end
|
318
|
+
base_namespace = args.pick(:base_namespace) do create_adhoc_base_namespace end
|
319
|
+
no_base_namespace = args.pick(:no_base_namespace) do false end
|
320
|
+
|
321
|
+
@hook_handler = self.create_hook_handler(
|
322
|
+
:base_namespace => base_namespace,
|
323
|
+
:privileges => privileges_,
|
324
|
+
:source => source
|
325
|
+
)
|
326
|
+
|
327
|
+
code = "nil;\n " + code
|
328
|
+
|
329
|
+
unless no_base_namespace
|
330
|
+
if (base_namespace.instance_of? Module)
|
331
|
+
code = "module #{base_namespace}\n #{code}\n end\n"
|
332
|
+
else
|
333
|
+
code = "class #{base_namespace}\n #{code}\n end\n"
|
334
|
+
end
|
335
|
+
end
|
392
336
|
|
393
|
-
|
394
|
-
|
395
|
-
handler.evalhook(code, binding_, source)
|
337
|
+
@hook_handler.evalhook(code, binding_, source)
|
396
338
|
end
|
397
339
|
rescue ::Timeout::Error
|
398
340
|
raise Shikashi::Timeout::Error
|
@@ -400,28 +342,9 @@ module Shikashi
|
|
400
342
|
end
|
401
343
|
end
|
402
344
|
|
345
|
+
end
|
403
346
|
|
404
|
-
#redirects a method with given name to a wrapper of the given class
|
405
|
-
#example:
|
406
|
-
# class PrintWrapper < Shikashi::Sandbox::MethodWrapper
|
407
|
-
# def call(*args)
|
408
|
-
# print "invoked print\n"
|
409
|
-
# original_call(*args)
|
410
|
-
# end
|
411
|
-
# end
|
412
|
-
#
|
413
|
-
# sandbox.redirect(:print, PrintWrapper)
|
414
|
-
# sandbox.redirect(:print, :wrapper_class => PrintWrapper)
|
415
|
-
# sandbox.redirect(:method_name => :print, :wrapper_class => PrintWrapper)
|
416
|
-
#
|
417
|
-
|
418
|
-
def redirect(*args)
|
419
|
-
mname = args.pick(Symbol, :method_name)
|
420
|
-
wclass = args.pick(Class, :wrapper_class)
|
421
|
-
@redirect_hash[mname] = wclass
|
422
|
-
end
|
423
347
|
|
424
|
-
end
|
425
348
|
end
|
426
349
|
|
427
350
|
Shikashi.global_binding = binding()
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shikashi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 19
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 3
|
9
9
|
- 0
|
10
|
-
version: 0.
|
10
|
+
version: 0.3.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Dario Seminara
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date:
|
18
|
+
date: 2011-01-16 00:00:00 -03:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -50,6 +50,22 @@ dependencies:
|
|
50
50
|
version: 0.1.0
|
51
51
|
type: :runtime
|
52
52
|
version_requirements: *id002
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: evalmimic
|
55
|
+
prerelease: false
|
56
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
hash: 27
|
62
|
+
segments:
|
63
|
+
- 0
|
64
|
+
- 1
|
65
|
+
- 0
|
66
|
+
version: 0.1.0
|
67
|
+
type: :runtime
|
68
|
+
version_requirements: *id003
|
53
69
|
description:
|
54
70
|
email: robertodarioseminara@gmail.com
|
55
71
|
executables: []
|
@@ -63,13 +79,12 @@ files:
|
|
63
79
|
- examples/basic/example3.rb
|
64
80
|
- examples/basic/example5.rb
|
65
81
|
- examples/basic/example4.rb
|
82
|
+
- examples/basic/example8.rb
|
66
83
|
- examples/basic/example6.rb
|
67
84
|
- examples/basic/example.rb
|
68
85
|
- examples/basic/example7.rb
|
69
86
|
- examples/basic/example2.rb
|
70
87
|
- examples/timeout/example1.rb
|
71
|
-
- examples/redir/example1.rb
|
72
|
-
- examples/redir/example2.rb
|
73
88
|
- lib/shikashi.rb
|
74
89
|
- lib/shikashi/pick_argument.rb
|
75
90
|
- lib/shikashi/sandbox.rb
|
@@ -77,7 +92,6 @@ files:
|
|
77
92
|
- lib/shikashi/privileges/singleton_methods.rb
|
78
93
|
- lib/shikashi/privileges/classes.rb
|
79
94
|
- lib/shikashi/privileges.rb
|
80
|
-
- test/functional/test_timeout.rb
|
81
95
|
- AUTHORS
|
82
96
|
- CHANGELOG
|
83
97
|
- README
|
@@ -116,6 +130,6 @@ rubyforge_project:
|
|
116
130
|
rubygems_version: 1.3.7
|
117
131
|
signing_key:
|
118
132
|
specification_version: 3
|
119
|
-
summary: shikashi is a ruby sandbox that permits the execution of "unprivileged" scripts by defining the permitted methods and constants the scripts can invoke
|
133
|
+
summary: shikashi is a ruby sandbox that permits the execution of "unprivileged" scripts by defining the permitted methods and constants the scripts can invoke with a white list logic
|
120
134
|
test_files: []
|
121
135
|
|
data/examples/redir/example1.rb
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
require "rubygems"
|
2
|
-
require "shikashi"
|
3
|
-
|
4
|
-
class TestWrapper < Shikashi::Sandbox::MethodWrapper
|
5
|
-
def call(*args)
|
6
|
-
print "called foo from source: #{source}, arguments: #{args.inspect} \n"
|
7
|
-
original_call(*args)
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
class X
|
12
|
-
def foo
|
13
|
-
print "original foo\n"
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
s = Shikashi::Sandbox.new
|
18
|
-
perm = Shikashi::Privileges.new
|
19
|
-
|
20
|
-
perm.object(X).allow :new
|
21
|
-
perm.instances_of(X).allow :foo
|
22
|
-
|
23
|
-
# redirect calls to foo to TestWrapper
|
24
|
-
perm.instances_of(X).redirect :foo, TestWrapper
|
25
|
-
|
26
|
-
s.run(perm,"X.new.foo")
|
data/examples/redir/example2.rb
DELETED
@@ -1,40 +0,0 @@
|
|
1
|
-
require "rubygems"
|
2
|
-
require "shikashi"
|
3
|
-
|
4
|
-
class TestWrapper < Shikashi::Sandbox::MethodWrapper
|
5
|
-
def call(*args)
|
6
|
-
print "called #{klass}#each block_given?:#{block_given?}, source: #{source}\n"
|
7
|
-
if block_given?
|
8
|
-
original_call(*args) do |*x|
|
9
|
-
print "yielded value #{x.first}\n"
|
10
|
-
yield(*x)
|
11
|
-
end
|
12
|
-
else
|
13
|
-
original_call(*args)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
s = Shikashi::Sandbox.new
|
19
|
-
perm = Shikashi::Privileges.new
|
20
|
-
|
21
|
-
perm.instances_of(Array).allow :each
|
22
|
-
perm.instances_of(Array).redirect :each, TestWrapper
|
23
|
-
|
24
|
-
perm.instances_of(Enumerable::Enumerator).allow :each
|
25
|
-
perm.instances_of(Enumerable::Enumerator).redirect :each, TestWrapper
|
26
|
-
|
27
|
-
perm.allow_method :print
|
28
|
-
|
29
|
-
s.run perm, '
|
30
|
-
array = [1,2,3]
|
31
|
-
|
32
|
-
array.each do |x|
|
33
|
-
print x,"\n"
|
34
|
-
end
|
35
|
-
|
36
|
-
enum = array.each
|
37
|
-
enum.each do |x|
|
38
|
-
print x,"\n"
|
39
|
-
end
|
40
|
-
'
|
@@ -1,36 +0,0 @@
|
|
1
|
-
require "test/unit"
|
2
|
-
require "shikashi"
|
3
|
-
|
4
|
-
class TimeoutTest < Test::Unit::TestCase
|
5
|
-
|
6
|
-
def _test_timeout(execution_delay, timeout)
|
7
|
-
priv = Shikashi::Privileges.new
|
8
|
-
# allow the execution of the method sleep to emulate an execution delay
|
9
|
-
priv.allow_method :sleep
|
10
|
-
|
11
|
-
if execution_delay > timeout
|
12
|
-
assert_raise Shikashi::Timeout::Error do
|
13
|
-
# specify the timeout and the current binding to use the execution_delay parameter
|
14
|
-
Shikashi::Sandbox.new.run("sleep execution_delay", priv, binding, :timeout => timeout)
|
15
|
-
end
|
16
|
-
else
|
17
|
-
assert_nothing_raised do
|
18
|
-
Shikashi::Sandbox.new.run("sleep execution_delay", priv, binding, :timeout => timeout)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def self.add_test(name, execution_delay, timeout)
|
24
|
-
define_method("test_"+name) do
|
25
|
-
_test_timeout(execution_delay, timeout)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
add_test "basic",2,1
|
30
|
-
add_test "float",0.2,0.1
|
31
|
-
add_test "float_no_hit",0.1,0.2
|
32
|
-
add_test "zero", 1,0
|
33
|
-
add_test "zero_no_hit", 0,1
|
34
|
-
|
35
|
-
end
|
36
|
-
|