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 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