shikashi 0.4.0 → 0.5.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 +6 -0
- data/README +5 -8
- data/Rakefile +2 -2
- data/examples/benchmark/bm1.rb +29 -0
- data/examples/benchmark/bm2.rb +25 -0
- data/lib/shikashi/sandbox.rb +121 -31
- metadata +9 -7
data/CHANGELOG
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
0.5.0 Refactored sandbox to keep base namespace on each run (see base_namespace example on README)
|
2
|
+
|
3
|
+
Code packet feature (see documentation)
|
4
|
+
|
5
|
+
Fixed spec to make it compatible with Ruby 1.9
|
6
|
+
|
1
7
|
0.4.0 Removed evalmimic dependency
|
2
8
|
|
3
9
|
Implemented sugar syntax for Sandbox and Privileges methods (see documentation)
|
data/README
CHANGED
@@ -167,9 +167,6 @@ define a class from inside the sandbox and use it from outside
|
|
167
167
|
|
168
168
|
include Shikashi
|
169
169
|
|
170
|
-
module SandboxModule
|
171
|
-
end
|
172
|
-
|
173
170
|
class X
|
174
171
|
def foo
|
175
172
|
print "X#foo\n"
|
@@ -177,23 +174,23 @@ define a class from inside the sandbox and use it from outside
|
|
177
174
|
end
|
178
175
|
|
179
176
|
s = Sandbox.new
|
180
|
-
priv = Privileges.new
|
181
|
-
priv.allow_method :print
|
182
177
|
|
183
178
|
s.run( "
|
184
|
-
class
|
179
|
+
class X
|
185
180
|
def foo
|
186
181
|
print \"foo defined inside the sandbox\\n\"
|
187
182
|
end
|
188
183
|
end
|
189
|
-
",
|
184
|
+
", Privileges.allow_method(:print))
|
190
185
|
|
191
186
|
|
192
187
|
x = X.new # X class is not affected by the sandbox (The X Class defined in the sandbox is SandboxModule::X)
|
193
188
|
x.foo
|
194
189
|
|
195
|
-
x =
|
190
|
+
x = s.base_namespace::X.new
|
196
191
|
x.foo
|
192
|
+
|
193
|
+
s.run("X.new.foo", Privileges.allow_method(:new).allow_method(:foo))
|
197
194
|
|
198
195
|
|
199
196
|
|
data/Rakefile
CHANGED
@@ -6,13 +6,13 @@ 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.5.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.5.0"
|
16
16
|
s.add_dependency "getsource", ">= 0.1.0"
|
17
17
|
s.has_rdoc = true
|
18
18
|
s.extra_rdoc_files = [ 'README' ]
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "shikashi"
|
3
|
+
require "benchmark"
|
4
|
+
|
5
|
+
code = "class X
|
6
|
+
def foo(n)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
X.new.foo(1000)
|
10
|
+
"
|
11
|
+
|
12
|
+
s = Shikashi::Sandbox.new
|
13
|
+
|
14
|
+
Benchmark.bm(7) do |x|
|
15
|
+
|
16
|
+
x.report("normal") {
|
17
|
+
1000.times do
|
18
|
+
s.run(code, Shikashi::Privileges.allow_method(:new))
|
19
|
+
end
|
20
|
+
}
|
21
|
+
|
22
|
+
x.report("packet") {
|
23
|
+
packet = s.packet(code, Shikashi::Privileges.allow_method(:new))
|
24
|
+
1000.times do
|
25
|
+
packet.run
|
26
|
+
end
|
27
|
+
}
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "shikashi"
|
3
|
+
require "benchmark"
|
4
|
+
|
5
|
+
s = Shikashi::Sandbox.new
|
6
|
+
|
7
|
+
class NilClass
|
8
|
+
def foo
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
Benchmark.bm(7) do |x|
|
13
|
+
|
14
|
+
x.report {
|
15
|
+
|
16
|
+
code = "
|
17
|
+
500000.times {
|
18
|
+
nil.foo
|
19
|
+
}
|
20
|
+
"
|
21
|
+
|
22
|
+
s.run code, Shikashi::Privileges.allow_method(:times).allow_method(:foo)
|
23
|
+
}
|
24
|
+
|
25
|
+
end
|
data/lib/shikashi/sandbox.rb
CHANGED
@@ -87,6 +87,10 @@ module Shikashi
|
|
87
87
|
def initialize
|
88
88
|
@privileges = Hash.new
|
89
89
|
@chain = Hash.new
|
90
|
+
@hook_handler = EvalhookHandler.new
|
91
|
+
@hook_handler.sandbox = self
|
92
|
+
@base_namespace = create_adhoc_base_namespace
|
93
|
+
@hook_handler.base_namespace = @base_namespace
|
90
94
|
end
|
91
95
|
|
92
96
|
# add a chain of sources, used internally
|
@@ -98,6 +102,38 @@ module Shikashi
|
|
98
102
|
@base_namespace
|
99
103
|
end
|
100
104
|
|
105
|
+
class Packet
|
106
|
+
def initialize(evalhook_packet, default_privileges, source) #:nodoc:
|
107
|
+
@evalhook_packet = evalhook_packet
|
108
|
+
@default_privileges = default_privileges
|
109
|
+
@source = source
|
110
|
+
end
|
111
|
+
|
112
|
+
#Run the code in the package
|
113
|
+
#
|
114
|
+
#call-seq: run(arguments)
|
115
|
+
#
|
116
|
+
#Arguments
|
117
|
+
#
|
118
|
+
# :binding Optional argument with the binding object of the context where the code is to be executed
|
119
|
+
# The default is a binding in the global context
|
120
|
+
# :timeout Optional argument to restrict the execution time of the script to a given value in seconds
|
121
|
+
def run(*args)
|
122
|
+
t = args.pick(:timeout) do nil end
|
123
|
+
binding_ = args.pick(Binding,:binding) do
|
124
|
+
nil
|
125
|
+
end
|
126
|
+
|
127
|
+
begin
|
128
|
+
timeout t do
|
129
|
+
@evalhook_packet.run(binding_, @source, 0)
|
130
|
+
end
|
131
|
+
rescue ::Timeout::Error
|
132
|
+
raise Shikashi::Timeout::Error
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
101
137
|
class EvalhookHandler < EvalHook::HookHandler
|
102
138
|
attr_accessor :sandbox
|
103
139
|
|
@@ -143,7 +179,8 @@ module Shikashi
|
|
143
179
|
source = get_caller
|
144
180
|
privileges = sandbox.privileges[source]
|
145
181
|
if privileges
|
146
|
-
|
182
|
+
constants = sandbox.base_namespace.constants
|
183
|
+
unless constants.include? name or constants.include? name.to_sym
|
147
184
|
unless privileges.const_read_allowed? name.to_s
|
148
185
|
raise SecurityError, "cannot access constant #{name}"
|
149
186
|
end
|
@@ -226,13 +263,10 @@ module Shikashi
|
|
226
263
|
end
|
227
264
|
end # Class
|
228
265
|
|
229
|
-
#Run the code in sandbox with the given privileges
|
230
|
-
#execution of classes and methods defined in the sandbox from outside the sandbox if a block is passed
|
266
|
+
#Run the code in sandbox with the given privileges
|
231
267
|
# (see examples)
|
232
268
|
#
|
233
|
-
#
|
234
|
-
#
|
235
|
-
#Arguments
|
269
|
+
# Arguments
|
236
270
|
#
|
237
271
|
# :code Mandatory argument of class String with the code to execute restricted in the sandbox
|
238
272
|
# :privileges Optional argument of class Shikashi::Sandbox::Privileges to indicate the restrictions of the
|
@@ -244,6 +278,9 @@ module Shikashi
|
|
244
278
|
# be specified as hash parameter
|
245
279
|
# :timeout Optional argument to restrict the execution time of the script to a given value in seconds
|
246
280
|
# (accepts integer and decimal values), when timeout hits Shikashi::Timeout::Error is raised
|
281
|
+
# :base_namespace Alternate module to contain all classes and constants defined by the unprivileged code
|
282
|
+
# if not specified, by default, the base_namespace is created with the sandbox itself
|
283
|
+
# :no_base_namespace Specify to do not use a base_namespace (default false, not recommended to change)
|
247
284
|
#
|
248
285
|
#
|
249
286
|
#The arguments can be passed in any order and using hash notation or not, examples:
|
@@ -286,29 +323,77 @@ module Shikashi
|
|
286
323
|
# end
|
287
324
|
# ', :privileges => privileges)
|
288
325
|
#
|
289
|
-
# #outside of this block, the method foo defined in the sandbox are invisible
|
290
|
-
# sandbox.run do
|
291
|
-
# self.foo
|
292
|
-
# end
|
293
|
-
#
|
294
326
|
#
|
295
327
|
def run(*args)
|
296
|
-
|
328
|
+
run_i(*args)
|
329
|
+
end
|
297
330
|
|
298
|
-
|
299
|
-
|
331
|
+
#Creates a packet of code with the given privileges to execute later as many times as neccessary
|
332
|
+
#
|
333
|
+
# (see examples)
|
334
|
+
#
|
335
|
+
# Arguments
|
336
|
+
#
|
337
|
+
# :code Mandatory argument of class String with the code to execute restricted in the sandbox
|
338
|
+
# :privileges Optional argument of class Shikashi::Sandbox::Privileges to indicate the restrictions of the
|
339
|
+
# code executed in the sandbox. The default is an empty Privileges (absolutly no permission)
|
340
|
+
# Must be of class Privileges or passed as hash_key (:privileges => privileges)
|
341
|
+
# :source Optional argument to indicate the "source name", (visible in the backtraces). Only can
|
342
|
+
# be specified as hash parameter
|
343
|
+
# :base_namespace Alternate module to contain all classes and constants defined by the unprivileged code
|
344
|
+
# if not specified, by default, the base_namespace is created with the sandbox itself
|
345
|
+
# :no_base_namespace Specify to do not use a base_namespace (default false, not recommended to change)
|
346
|
+
#
|
347
|
+
# NOTE: arguments are the same as for Sandbox#run method, except for timeout and binding which can be
|
348
|
+
# used when calling Shikashi::Sandbox::Packet#run
|
349
|
+
#
|
350
|
+
#Example:
|
351
|
+
#
|
352
|
+
# require "rubygems"
|
353
|
+
# require "shikashi"
|
354
|
+
#
|
355
|
+
# include Shikashi
|
356
|
+
#
|
357
|
+
# sandbox = Sandbox.new
|
358
|
+
#
|
359
|
+
# privileges = Privileges.allow_method(:print)
|
360
|
+
#
|
361
|
+
# # this is equivallent to sandbox.run('print "hello world\n"')
|
362
|
+
# packet = sandbox.packet('print "hello world\n"', privileges)
|
363
|
+
# packet.run
|
364
|
+
#
|
365
|
+
def packet(*args)
|
300
366
|
code = args.pick(String,:code)
|
367
|
+
base_namespace = args.pick(:base_namespace) do nil end
|
368
|
+
no_base_namespace = args.pick(:no_base_namespace) do @no_base_namespace end
|
369
|
+
privileges_ = args.pick(Privileges,:privileges) do Privileges.new end
|
301
370
|
|
302
|
-
|
303
|
-
base_namespace = args.pick(:base_namespace) do create_adhoc_base_namespace end
|
371
|
+
hook_handler = nil
|
304
372
|
|
305
|
-
|
306
|
-
|
373
|
+
if base_namespace
|
374
|
+
hook_handler = EvalhookHandler.new
|
375
|
+
hook_handler.base_namespace = base_namespace
|
376
|
+
hook_handler.sandbox = self
|
377
|
+
else
|
378
|
+
hook_handler = @hook_handler
|
379
|
+
base_namespace = hook_handler.base_namespace
|
380
|
+
end
|
381
|
+
source = args.pick(:source) do generate_id end
|
382
|
+
|
383
|
+
self.privileges[source] = privileges_
|
384
|
+
|
385
|
+
code = "nil;\n " + code
|
386
|
+
|
387
|
+
unless no_base_namespace
|
388
|
+
if (eval(base_namespace.to_s).instance_of? Module)
|
389
|
+
code = "module #{base_namespace}\n #{code}\n end\n"
|
390
|
+
else
|
391
|
+
code = "class #{base_namespace}\n #{code}\n end\n"
|
392
|
+
end
|
307
393
|
end
|
308
|
-
@base_namespace = base_namespace
|
309
|
-
no_base_namespace = args.pick(:no_base_namespace) do false end
|
310
394
|
|
311
|
-
|
395
|
+
evalhook_packet = @hook_handler.packet(code)
|
396
|
+
Shikashi::Sandbox::Packet.new(evalhook_packet, privileges_, source)
|
312
397
|
end
|
313
398
|
|
314
399
|
def create_hook_handler(*args)
|
@@ -338,7 +423,6 @@ private
|
|
338
423
|
|
339
424
|
def run_i(*args)
|
340
425
|
|
341
|
-
|
342
426
|
t = args.pick(:timeout) do nil end
|
343
427
|
raise Shikashi::Timeout::Error if t == 0
|
344
428
|
t = t || 0
|
@@ -352,26 +436,32 @@ private
|
|
352
436
|
code = args.pick(String,:code)
|
353
437
|
binding_ = args.pick(Binding,:binding) do Shikashi.global_binding end
|
354
438
|
source = args.pick(:source) do generate_id end
|
355
|
-
base_namespace = args.pick(:base_namespace) do
|
356
|
-
no_base_namespace = args.pick(:no_base_namespace) do
|
439
|
+
base_namespace = args.pick(:base_namespace) do nil end
|
440
|
+
no_base_namespace = args.pick(:no_base_namespace) do @no_base_namespace end
|
357
441
|
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
442
|
+
hook_handler = nil
|
443
|
+
|
444
|
+
if base_namespace
|
445
|
+
hook_handler = EvalhookHandler.new
|
446
|
+
hook_handler.base_namespace = base_namespace
|
447
|
+
hook_handler.sandbox = self
|
448
|
+
else
|
449
|
+
hook_handler = @hook_handler
|
450
|
+
base_namespace = hook_handler.base_namespace
|
451
|
+
end
|
363
452
|
|
453
|
+
self.privileges[source] = privileges_
|
364
454
|
code = "nil;\n " + code
|
365
455
|
|
366
456
|
unless no_base_namespace
|
367
|
-
if (base_namespace.instance_of? Module)
|
457
|
+
if (eval(base_namespace.to_s).instance_of? Module)
|
368
458
|
code = "module #{base_namespace}\n #{code}\n end\n"
|
369
459
|
else
|
370
460
|
code = "class #{base_namespace}\n #{code}\n end\n"
|
371
461
|
end
|
372
462
|
end
|
373
463
|
|
374
|
-
|
464
|
+
hook_handler.evalhook(code, binding_, source)
|
375
465
|
end
|
376
466
|
rescue ::Timeout::Error
|
377
467
|
raise Shikashi::Timeout::Error
|
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: 11
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 5
|
9
9
|
- 0
|
10
|
-
version: 0.
|
10
|
+
version: 0.5.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-06-20 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: 11
|
30
30
|
segments:
|
31
31
|
- 0
|
32
|
-
-
|
32
|
+
- 5
|
33
33
|
- 0
|
34
|
-
version: 0.
|
34
|
+
version: 0.5.0
|
35
35
|
type: :runtime
|
36
36
|
version_requirements: *id001
|
37
37
|
- !ruby/object:Gem::Dependency
|
@@ -59,6 +59,8 @@ extensions: []
|
|
59
59
|
extra_rdoc_files:
|
60
60
|
- README
|
61
61
|
files:
|
62
|
+
- examples/benchmark/bm1.rb
|
63
|
+
- examples/benchmark/bm2.rb
|
62
64
|
- examples/basic/example1.rb
|
63
65
|
- examples/basic/example3.rb
|
64
66
|
- examples/basic/example5.rb
|