shikashi 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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 ::X
179
+ class X
185
180
  def foo
186
181
  print \"foo defined inside the sandbox\\n\"
187
182
  end
188
183
  end
189
- ", priv, :base_namespace => SandboxModule)
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 = SandboxModule::X.new
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.4.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.4.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
@@ -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
- unless sandbox.base_namespace.constants.include? name
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, also run privileged code in the sandbox context for
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
- #call-seq: run(arguments)
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
- newargs = Array.new
328
+ run_i(*args)
329
+ end
297
330
 
298
- timeout = args.pick(:timeout) do nil end
299
- privileges_ = args.pick(Privileges,:privileges) do Privileges.new end
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
- source = args.pick(:source) do nil end
303
- base_namespace = args.pick(:base_namespace) do create_adhoc_base_namespace end
371
+ hook_handler = nil
304
372
 
305
- binding_ = args.pick(Binding,:binding) do
306
- nil
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
- run_i(code, privileges_, binding_, :base_namespace => base_namespace, :timeout => timeout, :no_base_namespace => no_base_namespace)
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 create_adhoc_base_namespace end
356
- no_base_namespace = args.pick(:no_base_namespace) do false end
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
- @hook_handler = self.create_hook_handler(
359
- :base_namespace => base_namespace,
360
- :privileges => privileges_,
361
- :source => source
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
- @hook_handler.evalhook(code, binding_, source)
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: 15
4
+ hash: 11
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 4
8
+ - 5
9
9
  - 0
10
- version: 0.4.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-04-28 00:00:00 -03:00
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: 15
29
+ hash: 11
30
30
  segments:
31
31
  - 0
32
- - 4
32
+ - 5
33
33
  - 0
34
- version: 0.4.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