shikashi 0.3.1 → 0.4.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/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: []
|