shikashi 0.3.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +10 -0
- data/Rakefile +2 -3
- data/examples/basic/example3.rb +6 -8
- data/examples/basic/example6.rb +5 -8
- data/examples/basic/example7.rb +4 -6
- data/examples/basic/example8.rb +2 -6
- data/lib/shikashi/privileges.rb +111 -18
- data/lib/shikashi/sandbox.rb +47 -10
- metadata +8 -24
data/CHANGELOG
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
0.4.0 Removed evalmimic dependency
|
2
|
+
|
3
|
+
Implemented sugar syntax for Sandbox and Privileges methods (see documentation)
|
4
|
+
|
5
|
+
Added control over READ access to constants
|
6
|
+
|
7
|
+
Added control over READ access to global variables
|
8
|
+
|
1
9
|
0.3.1 Fixed package info
|
2
10
|
|
3
11
|
0.3.0 Added enclosure by base_namespace parameter (see examples)
|
@@ -5,6 +13,8 @@
|
|
5
13
|
shikashi HookHandler opened to allow use it in nested hook handlers
|
6
14
|
|
7
15
|
Added restriction to avoid the system calls when using %x[] and ``
|
16
|
+
|
17
|
+
0.2.0 shikashi HookHandler opened to allow use it in nested hook handlers
|
8
18
|
|
9
19
|
0.2.0 Added control over the access to constants and global variables (See documentation)
|
10
20
|
|
data/Rakefile
CHANGED
@@ -6,15 +6,14 @@ 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.4.0'
|
10
10
|
s.author = 'Dario Seminara'
|
11
11
|
s.email = 'robertodarioseminara@gmail.com'
|
12
12
|
s.platform = Gem::Platform::RUBY
|
13
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
|
-
s.add_dependency "evalhook", ">= 0.
|
15
|
+
s.add_dependency "evalhook", ">= 0.4.0"
|
16
16
|
s.add_dependency "getsource", ">= 0.1.0"
|
17
|
-
s.add_dependency "evalmimic", ">= 0.1.0"
|
18
17
|
s.has_rdoc = true
|
19
18
|
s.extra_rdoc_files = [ 'README' ]
|
20
19
|
# s.rdoc_options << '--main' << 'README'
|
data/examples/basic/example3.rb
CHANGED
@@ -6,10 +6,6 @@ require "shikashi"
|
|
6
6
|
include Shikashi
|
7
7
|
|
8
8
|
s = Sandbox.new
|
9
|
-
priv = Privileges.new
|
10
|
-
|
11
|
-
# allow execution of print
|
12
|
-
priv.allow_method :print
|
13
9
|
|
14
10
|
class X
|
15
11
|
def foo
|
@@ -25,12 +21,14 @@ class X
|
|
25
21
|
system("echo privileged operation > " + out)
|
26
22
|
end
|
27
23
|
end
|
28
|
-
# allow method new of class X
|
29
|
-
priv.object(X).allow :new
|
30
24
|
|
31
|
-
# allow instance methods of X. Note that the method privileged_operations is not allowed
|
32
|
-
priv.instances_of(X).allow :foo, :bar
|
33
25
|
|
26
|
+
priv = Privileges.
|
27
|
+
allow_method(:print). # allow execution of print
|
28
|
+
object(X).allow(:new). # allow method new of class X
|
29
|
+
instances_of(X).allow(:foo, :bar). # allow instance methods of X. Note that the method privileged_operations is not allowed
|
30
|
+
allow_const_read("X","SecurityError") # allow the access of X constant
|
31
|
+
|
34
32
|
#inside the sandbox, only can use method foo on main and method times on instances of Fixnum
|
35
33
|
s.run(priv, '
|
36
34
|
x = X.new
|
data/examples/basic/example6.rb
CHANGED
@@ -5,15 +5,12 @@ require "shikashi"
|
|
5
5
|
|
6
6
|
include Shikashi
|
7
7
|
|
8
|
-
|
9
|
-
priv
|
10
|
-
|
11
|
-
priv.allow_method :print
|
12
|
-
priv.allow_global :$a
|
13
|
-
|
14
|
-
s.run(priv, '
|
8
|
+
priv = Privileges.allow_method(:print).allow_global_write(:$a)
|
9
|
+
Sandbox.run(priv,
|
10
|
+
'
|
15
11
|
$a = 9
|
16
12
|
print "assigned 9 to $a\n"
|
17
|
-
'
|
13
|
+
'
|
14
|
+
)
|
18
15
|
|
19
16
|
p $a
|
data/examples/basic/example7.rb
CHANGED
@@ -3,13 +3,11 @@ require "shikashi"
|
|
3
3
|
|
4
4
|
include Shikashi
|
5
5
|
|
6
|
-
|
7
|
-
|
6
|
+
priv = Privileges.
|
7
|
+
allow_method(:print).
|
8
|
+
allow_const_write("Object::A")
|
8
9
|
|
9
|
-
priv
|
10
|
-
priv.allow_const "Object::A"
|
11
|
-
|
12
|
-
s.run(priv, '
|
10
|
+
Sandbox.run(priv, '
|
13
11
|
print "assigned 8 to Object::A\n"
|
14
12
|
A = 8
|
15
13
|
')
|
data/examples/basic/example8.rb
CHANGED
@@ -12,17 +12,13 @@ class X
|
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
-
|
16
|
-
priv = Privileges.new
|
17
|
-
priv.allow_method :print
|
18
|
-
|
19
|
-
s.run( "
|
15
|
+
Sandbox.new.run( "
|
20
16
|
class ::X
|
21
17
|
def foo
|
22
18
|
print \"foo defined inside the sandbox\\n\"
|
23
19
|
end
|
24
20
|
end
|
25
|
-
",
|
21
|
+
", Privileges.allow_method(:print), :base_namespace => SandboxModule)
|
26
22
|
|
27
23
|
|
28
24
|
x = X.new # X class is not affected by the sandbox (The X Class defined in the sandbox is SandboxModule::X)
|
data/lib/shikashi/privileges.rb
CHANGED
@@ -40,7 +40,8 @@ public
|
|
40
40
|
|
41
41
|
# Used in Privileges to store information about specified method permissions
|
42
42
|
class AllowedMethods
|
43
|
-
def initialize
|
43
|
+
def initialize(privileges = nil)
|
44
|
+
@privileges = privileges
|
44
45
|
@allowed_methods = Array.new
|
45
46
|
@redirect_hash = Hash.new
|
46
47
|
@all = false
|
@@ -78,11 +79,15 @@ public
|
|
78
79
|
method_names.each do |mn|
|
79
80
|
@allowed_methods << mn
|
80
81
|
end
|
82
|
+
|
83
|
+
@privileges
|
81
84
|
end
|
82
85
|
|
83
86
|
#Specifies that any method is allowed
|
84
87
|
def allow_all
|
85
88
|
@all = true
|
89
|
+
|
90
|
+
@privileges
|
86
91
|
end
|
87
92
|
|
88
93
|
end
|
@@ -94,15 +99,17 @@ public
|
|
94
99
|
@allowed_instances = Hash.new
|
95
100
|
@allowed_methods = Array.new
|
96
101
|
@allowed_klass_methods = Hash.new
|
97
|
-
@
|
98
|
-
@
|
102
|
+
@allowed_read_globals = Array.new
|
103
|
+
@allowed_read_consts = Array.new
|
104
|
+
@allowed_write_globals = Array.new
|
105
|
+
@allowed_write_consts = Array.new
|
99
106
|
end
|
100
107
|
|
101
108
|
private
|
102
109
|
def hash_entry(hash, key)
|
103
110
|
tmp = hash[key]
|
104
111
|
unless tmp
|
105
|
-
tmp = AllowedMethods.new
|
112
|
+
tmp = AllowedMethods.new(self)
|
106
113
|
hash[key] = tmp
|
107
114
|
end
|
108
115
|
tmp
|
@@ -168,7 +175,8 @@ public
|
|
168
175
|
#
|
169
176
|
|
170
177
|
def allow_method(method_name)
|
171
|
-
@allowed_methods << method_name
|
178
|
+
@allowed_methods << method_name.to_sym
|
179
|
+
self
|
172
180
|
end
|
173
181
|
|
174
182
|
def allow?(klass, recv, method_name, method_id)
|
@@ -254,15 +262,23 @@ public
|
|
254
262
|
@xstr_allowed
|
255
263
|
end
|
256
264
|
|
257
|
-
def
|
258
|
-
@
|
265
|
+
def global_read_allowed?(varname)
|
266
|
+
@allowed_read_globals.include? varname
|
267
|
+
end
|
268
|
+
|
269
|
+
def global_write_allowed?(varname)
|
270
|
+
@allowed_write_globals.include? varname
|
259
271
|
end
|
260
272
|
|
261
|
-
def
|
262
|
-
@
|
273
|
+
def const_read_allowed?(varname)
|
274
|
+
@allowed_read_consts.include? varname
|
263
275
|
end
|
264
276
|
|
265
|
-
|
277
|
+
def const_write_allowed?(varname)
|
278
|
+
@allowed_write_consts.include? varname
|
279
|
+
end
|
280
|
+
|
281
|
+
# Enables the permissions needed to execute system calls from the script
|
266
282
|
#
|
267
283
|
# Example:
|
268
284
|
#
|
@@ -274,11 +290,19 @@ public
|
|
274
290
|
# s.run(priv, '
|
275
291
|
# %x[ls -l]
|
276
292
|
# ')
|
293
|
+
#
|
294
|
+
#
|
295
|
+
# Example 2:
|
296
|
+
#
|
297
|
+
# Sandbox.run('%x[ls -l]', Privileges.allow_xstr)
|
298
|
+
#
|
277
299
|
def allow_xstr
|
278
300
|
@xstr_allowed = true
|
301
|
+
|
302
|
+
self
|
279
303
|
end
|
280
304
|
|
281
|
-
#
|
305
|
+
# Enables the permissions needed to read one or more global variables
|
282
306
|
#
|
283
307
|
# Example:
|
284
308
|
#
|
@@ -286,7 +310,38 @@ public
|
|
286
310
|
# priv = Privileges.new
|
287
311
|
#
|
288
312
|
# priv.allow_method :print
|
289
|
-
# priv.
|
313
|
+
# priv.allow_global_read :$a
|
314
|
+
#
|
315
|
+
# $a = 9
|
316
|
+
#
|
317
|
+
# s.run(priv, '
|
318
|
+
# print "$a value:", $a, "s\n"
|
319
|
+
# ')
|
320
|
+
#
|
321
|
+
# Example 2
|
322
|
+
#
|
323
|
+
# Sandbox.run('
|
324
|
+
# print "$a value:", $a, "s\n"
|
325
|
+
# print "$b value:", $b, "s\n"
|
326
|
+
# ', Privileges.allow_global_read(:$a,:$b) )
|
327
|
+
#
|
328
|
+
def allow_global_read( *varnames )
|
329
|
+
varnames.each do |varname|
|
330
|
+
@allowed_read_globals << varname.to_sym
|
331
|
+
end
|
332
|
+
|
333
|
+
self
|
334
|
+
end
|
335
|
+
|
336
|
+
# Enables the permissions needed to create or change one or more global variables
|
337
|
+
#
|
338
|
+
# Example:
|
339
|
+
#
|
340
|
+
# s = Sandbox.new
|
341
|
+
# priv = Privileges.new
|
342
|
+
#
|
343
|
+
# priv.allow_method :print
|
344
|
+
# priv.allow_global_write :$a
|
290
345
|
#
|
291
346
|
# s.run(priv, '
|
292
347
|
# $a = 9
|
@@ -295,18 +350,23 @@ public
|
|
295
350
|
#
|
296
351
|
# p $a
|
297
352
|
#
|
298
|
-
def
|
299
|
-
|
353
|
+
def allow_global_write( *varnames )
|
354
|
+
varnames.each do |varname|
|
355
|
+
@allowed_write_globals << varname.to_sym
|
356
|
+
end
|
357
|
+
|
358
|
+
self
|
300
359
|
end
|
301
360
|
|
302
|
-
|
361
|
+
|
362
|
+
# Enables the permissions needed to create or change one or more constants
|
303
363
|
#
|
304
364
|
# Example:
|
305
365
|
# s = Sandbox.new
|
306
366
|
# priv = Privileges.new
|
307
367
|
#
|
308
368
|
# priv.allow_method :print
|
309
|
-
# priv.
|
369
|
+
# priv.allow_const_write "Object::A"
|
310
370
|
#
|
311
371
|
# s.run(priv, '
|
312
372
|
# print "assigned 8 to Object::A\n"
|
@@ -315,10 +375,43 @@ public
|
|
315
375
|
#
|
316
376
|
# p A
|
317
377
|
|
318
|
-
def
|
319
|
-
|
378
|
+
def allow_const_write( *varnames )
|
379
|
+
varnames.each do |varname|
|
380
|
+
@allowed_write_consts << varname.to_s
|
381
|
+
end
|
382
|
+
self
|
383
|
+
end
|
384
|
+
|
385
|
+
# Enables the permissions needed to read one or more constants
|
386
|
+
#
|
387
|
+
# Example:
|
388
|
+
# s = Sandbox.new
|
389
|
+
# priv = Privileges.new
|
390
|
+
#
|
391
|
+
# priv.allow_method :print
|
392
|
+
# priv.allow_const_read "Object::A"
|
393
|
+
#
|
394
|
+
# A = 8
|
395
|
+
# s.run(priv, '
|
396
|
+
# print "assigned Object::A:", A,"\n"
|
397
|
+
# ')
|
398
|
+
#
|
399
|
+
def allow_const_read( *varnames )
|
400
|
+
varnames.each do |varname|
|
401
|
+
@allowed_read_consts << varname.to_s
|
402
|
+
end
|
403
|
+
|
404
|
+
self
|
320
405
|
end
|
321
406
|
|
407
|
+
|
408
|
+
class << self
|
409
|
+
(Shikashi::Privileges.instance_methods - Object.instance_methods).each do |mname|
|
410
|
+
define_method(mname) do |*args|
|
411
|
+
Shikashi::Privileges.new.send(mname, *args)
|
412
|
+
end
|
413
|
+
end
|
414
|
+
end
|
322
415
|
end
|
323
416
|
|
324
417
|
end
|
data/lib/shikashi/sandbox.rb
CHANGED
@@ -24,7 +24,6 @@ require "shikashi/privileges"
|
|
24
24
|
require "shikashi/pick_argument"
|
25
25
|
require "getsource"
|
26
26
|
require "timeout"
|
27
|
-
require "evalmimic"
|
28
27
|
|
29
28
|
module Shikashi
|
30
29
|
|
@@ -71,6 +70,13 @@ module Shikashi
|
|
71
70
|
attr_reader :hook_handler
|
72
71
|
|
73
72
|
#
|
73
|
+
# Same as Sandbox.new.run
|
74
|
+
#
|
75
|
+
|
76
|
+
def self.run(*args)
|
77
|
+
Sandbox.new.run(Shikashi.global_binding, *args)
|
78
|
+
end
|
79
|
+
#
|
74
80
|
# Generate a random source file name for the sandbox, used internally
|
75
81
|
#
|
76
82
|
|
@@ -113,7 +119,7 @@ module Shikashi
|
|
113
119
|
|
114
120
|
privileges = sandbox.privileges[source]
|
115
121
|
if privileges
|
116
|
-
unless privileges.
|
122
|
+
unless privileges.global_write_allowed? global_id
|
117
123
|
raise SecurityError.new("Cannot assign global variable #{global_id}")
|
118
124
|
end
|
119
125
|
end
|
@@ -121,13 +127,45 @@ module Shikashi
|
|
121
127
|
nil
|
122
128
|
end
|
123
129
|
|
130
|
+
def handle_gvar(global_id)
|
131
|
+
source = get_caller
|
132
|
+
privileges = sandbox.privileges[source]
|
133
|
+
if privileges
|
134
|
+
unless privileges.global_read_allowed? global_id
|
135
|
+
raise SecurityError, "cannot access global variable #{global_id}"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
nil
|
140
|
+
end
|
141
|
+
|
142
|
+
def handle_const(name)
|
143
|
+
source = get_caller
|
144
|
+
privileges = sandbox.privileges[source]
|
145
|
+
if privileges
|
146
|
+
unless sandbox.base_namespace.constants.include? name
|
147
|
+
unless privileges.const_read_allowed? name.to_s
|
148
|
+
raise SecurityError, "cannot access constant #{name}"
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
const_value(sandbox.base_namespace.const_get(name))
|
154
|
+
end
|
155
|
+
|
124
156
|
def handle_cdecl(klass, const_id, value)
|
125
157
|
source = get_caller
|
126
158
|
|
127
159
|
privileges = sandbox.privileges[source]
|
128
160
|
if privileges
|
129
|
-
unless privileges.
|
130
|
-
|
161
|
+
unless privileges.const_write_allowed? "#{klass}::#{const_id}"
|
162
|
+
if (klass == Object)
|
163
|
+
unless privileges.const_write_allowed? const_id.to_s
|
164
|
+
raise SecurityError.new("Cannot assign const #{const_id}")
|
165
|
+
end
|
166
|
+
else
|
167
|
+
raise SecurityError.new("Cannot assign const #{klass}::#{const_id}")
|
168
|
+
end
|
131
169
|
end
|
132
170
|
end
|
133
171
|
|
@@ -255,19 +293,18 @@ module Shikashi
|
|
255
293
|
#
|
256
294
|
#
|
257
295
|
def run(*args)
|
258
|
-
end
|
259
|
-
|
260
|
-
define_eval_method :run
|
261
|
-
def internal_eval(b_, args)
|
262
|
-
|
263
296
|
newargs = Array.new
|
264
297
|
|
265
298
|
timeout = args.pick(:timeout) do nil end
|
266
299
|
privileges_ = args.pick(Privileges,:privileges) do Privileges.new end
|
267
300
|
code = args.pick(String,:code)
|
268
|
-
|
301
|
+
|
269
302
|
source = args.pick(:source) do nil end
|
270
303
|
base_namespace = args.pick(:base_namespace) do create_adhoc_base_namespace end
|
304
|
+
|
305
|
+
binding_ = args.pick(Binding,:binding) do
|
306
|
+
nil
|
307
|
+
end
|
271
308
|
@base_namespace = base_namespace
|
272
309
|
no_base_namespace = args.pick(:no_base_namespace) do false end
|
273
310
|
|
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: 15
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
8
|
+
- 4
|
9
|
+
- 0
|
10
|
+
version: 0.4.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: 2011-
|
18
|
+
date: 2011-04-28 00:00:00 -03:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -26,12 +26,12 @@ dependencies:
|
|
26
26
|
requirements:
|
27
27
|
- - ">="
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
hash:
|
29
|
+
hash: 15
|
30
30
|
segments:
|
31
31
|
- 0
|
32
|
-
-
|
32
|
+
- 4
|
33
33
|
- 0
|
34
|
-
version: 0.
|
34
|
+
version: 0.4.0
|
35
35
|
type: :runtime
|
36
36
|
version_requirements: *id001
|
37
37
|
- !ruby/object:Gem::Dependency
|
@@ -50,22 +50,6 @@ 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
|
69
53
|
description:
|
70
54
|
email: robertodarioseminara@gmail.com
|
71
55
|
executables: []
|