shikashi 0.2.0 → 0.3.0
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.
- 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
|
-
|