shikashi-the-north 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,16 @@
1
+ # "hello world" from within the sandbox
2
+
3
+ require "rubygems"
4
+ require "shikashi"
5
+
6
+ include Shikashi
7
+
8
+ priv = Privileges.allow_method(:print).allow_global_write(:$a)
9
+ Sandbox.run(priv,
10
+ '
11
+ $a = 9
12
+ print "assigned 9 to $a\n"
13
+ '
14
+ )
15
+
16
+ p $a
@@ -0,0 +1,15 @@
1
+ require "rubygems"
2
+ require "shikashi"
3
+
4
+ include Shikashi
5
+
6
+ priv = Privileges.
7
+ allow_method(:print).
8
+ allow_const_write("Object::A")
9
+
10
+ Sandbox.run(priv, '
11
+ print "assigned 8 to Object::A\n"
12
+ A = 8
13
+ ')
14
+ p A
15
+
@@ -0,0 +1,29 @@
1
+ require "rubygems"
2
+ require "shikashi"
3
+
4
+ include Shikashi
5
+
6
+ module SandboxModule
7
+ end
8
+
9
+ class X
10
+ def foo
11
+ print "X#foo\n"
12
+ end
13
+ end
14
+
15
+ Sandbox.new.run( "
16
+ class ::X
17
+ def foo
18
+ print \"foo defined inside the sandbox\\n\"
19
+ end
20
+ end
21
+ ", Privileges.allow_method(:print), :base_namespace => SandboxModule)
22
+
23
+
24
+ x = X.new # X class is not affected by the sandbox (The X Class defined in the sandbox is SandboxModule::X)
25
+ x.foo
26
+
27
+ x = SandboxModule::X.new
28
+ x.foo
29
+
@@ -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
@@ -0,0 +1,9 @@
1
+ require "rubygems"
2
+ require "shikashi"
3
+
4
+ s = Shikashi::Sandbox.new
5
+ perm = Shikashi::Privileges.new
6
+
7
+ perm.allow_method :sleep
8
+
9
+ s.run(perm,"sleep 3", :timeout => 2) # raise Shikashi::Timeout::Error after 2 seconds
@@ -0,0 +1,23 @@
1
+ =begin
2
+
3
+ This file is part of the shikashi project, http://github.com/tario/shikashi
4
+
5
+ Copyright (c) 2009-2010 Roberto Dario Seminara <robertodarioseminara@gmail.com>
6
+
7
+ shikashi is free software: you can redistribute it and/or modify
8
+ it under the terms of the gnu general public license as published by
9
+ the free software foundation, either version 3 of the license, or
10
+ (at your option) any later version.
11
+
12
+ shikashi is distributed in the hope that it will be useful,
13
+ but without any warranty; without even the implied warranty of
14
+ merchantability or fitness for a particular purpose. see the
15
+ gnu general public license for more details.
16
+
17
+ you should have received a copy of the gnu general public license
18
+ along with shikashi. if not, see <http://www.gnu.org/licenses/>.
19
+
20
+ =end
21
+ require "shikashi/sandbox"
22
+
23
+
@@ -0,0 +1,81 @@
1
+ =begin
2
+
3
+ This file is part of the shikashi project, http://github.com/tario/shikashi
4
+
5
+ Copyright (c) 2009-2010 Roberto Dario Seminara <robertodarioseminara@gmail.com>
6
+
7
+ shikashi is free software: you can redistribute it and/or modify
8
+ it under the terms of the gnu general public license as published by
9
+ the free software foundation, either version 3 of the license, or
10
+ (at your option) any later version.
11
+
12
+ shikashi is distributed in the hope that it will be useful,
13
+ but without any warranty; without even the implied warranty of
14
+ merchantability or fitness for a particular purpose. see the
15
+ gnu general public license for more details.
16
+
17
+ you should have received a copy of the gnu general public license
18
+ along with shikashi. if not, see <http://www.gnu.org/licenses/>.
19
+
20
+ =end
21
+ module Arguments
22
+ module NoDefault
23
+ end
24
+ end
25
+ class Array
26
+
27
+ def pick_by_class(klass)
28
+ klassary = self.select{|x| x.instance_of? klass}
29
+ if klassary.size > 1
30
+ raise ArgumentError, "ambiguous parameters of class #{klass}"
31
+ elsif klassary.size == 1
32
+ klassary.first
33
+ else
34
+ nil
35
+ end
36
+
37
+ end
38
+
39
+ def pick(*args)
40
+
41
+ klass = args.pick_by_class Class
42
+ hash_key = args.pick_by_class Symbol
43
+
44
+ ary = []
45
+
46
+ if klass
47
+ ary = self.select{|x| x.instance_of? klass}
48
+
49
+ if ary.size > 1
50
+ raise ArgumentError, "ambiguous parameters of class #{klass}"
51
+ end
52
+ else
53
+ ary = []
54
+ end
55
+
56
+ if hash_key
57
+ each do |x|
58
+ if x.instance_of? Hash
59
+ if x[hash_key]
60
+ ary << x[hash_key]
61
+ end
62
+ end
63
+ end
64
+
65
+ if ary.size > 1
66
+ raise ArgumentError, "ambiguous parameters of class #{klass} and key '#{hash_key}'"
67
+ end
68
+
69
+ end
70
+
71
+ if ary.size == 1
72
+ return ary.first
73
+ end
74
+
75
+ unless block_given?
76
+ raise ArgumentError, "missing mandatory argument '#{hash_key}' or of class #{klass}"
77
+ end
78
+
79
+ yield
80
+ end
81
+ end
@@ -0,0 +1,418 @@
1
+ =begin
2
+
3
+ This file is part of the shikashi project, http://github.com/tario/shikashi
4
+
5
+ Copyright (c) 2009-2010 Roberto Dario Seminara <robertodarioseminara@gmail.com>
6
+
7
+ shikashi is free software: you can redistribute it and/or modify
8
+ it under the terms of the gnu general public license as published by
9
+ the free software foundation, either version 3 of the license, or
10
+ (at your option) any later version.
11
+
12
+ shikashi is distributed in the hope that it will be useful,
13
+ but without any warranty; without even the implied warranty of
14
+ merchantability or fitness for a particular purpose. see the
15
+ gnu general public license for more details.
16
+
17
+ you should have received a copy of the gnu general public license
18
+ along with shikashi. if not, see <http://www.gnu.org/licenses/>.
19
+
20
+ =end
21
+ require "find"
22
+
23
+ module Shikashi
24
+ #
25
+ #The Privileges class represent permissions about methods and objects
26
+ #
27
+ class Privileges
28
+
29
+ private
30
+ def self.load_privilege_packages
31
+ Find.find(__FILE__.split("/")[0..-2].join("/") + "/privileges" ) do |path|
32
+ if path =~ /\.rb$/
33
+ require path
34
+ end
35
+ end
36
+ end
37
+
38
+ load_privilege_packages
39
+ public
40
+
41
+ # Used in Privileges to store information about specified method permissions
42
+ class AllowedMethods
43
+ def initialize(privileges = nil)
44
+ @privileges = privileges
45
+ @allowed_methods = Array.new
46
+ @redirect_hash = Hash.new
47
+ @all = false
48
+ end
49
+
50
+ #return true if the method named method_name is allowed
51
+ #Example
52
+ #
53
+ # allowed_methods = AllowedMethods.new
54
+ # allowed_methods.allowed? :foo # => false
55
+ # allowed_methods.allow :foo
56
+ # allowed_methods.allowed? :foo # => true
57
+ # allowed_methods.allow_all
58
+ # allowed_methods.allowed? :bar # => true
59
+ #
60
+ # Privileges#instance_of, Privileges#methods_of and Privileges#object returns the corresponding
61
+ # instance of AllowedMethods
62
+ def allowed?(method_name)
63
+ if @all
64
+ true
65
+ else
66
+ @allowed_methods.include?(method_name)
67
+ end
68
+ end
69
+
70
+ #Specifies that a method or list of methods are allowed
71
+ #Example
72
+ #
73
+ # allowed_methods = AllowedMethods.new
74
+ # allowed_methods.allow :foo
75
+ # allowed_methods.allow :foo, :bar
76
+ # allowed_methods.allow :foo, :bar, :test
77
+ #
78
+ def allow(*method_names)
79
+ method_names.each do |mn|
80
+ @allowed_methods << mn
81
+ end
82
+
83
+ @privileges
84
+ end
85
+
86
+ #Specifies that any method is allowed
87
+ def allow_all
88
+ @all = true
89
+
90
+ @privileges
91
+ end
92
+
93
+ end
94
+
95
+ def initialize
96
+ @allowed_objects = Hash.new
97
+ @allowed_kinds = Hash.new
98
+ @allowed_classes = Hash.new
99
+ @allowed_instances = Hash.new
100
+ @allowed_methods = Array.new
101
+ @allowed_klass_methods = Hash.new
102
+ @allowed_read_globals = Array.new
103
+ @allowed_read_consts = Array.new
104
+ @allowed_write_globals = Array.new
105
+ @allowed_write_consts = Array.new
106
+ end
107
+
108
+ private
109
+ def hash_entry(hash, key)
110
+ tmp = hash[key]
111
+ unless tmp
112
+ tmp = AllowedMethods.new(self)
113
+ hash[key] = tmp
114
+ end
115
+ tmp
116
+ end
117
+ public
118
+
119
+ #
120
+ #Specifies the methods allowed for an specific object
121
+ #
122
+ #Example 1:
123
+ # privileges.object(Hash).allow :new
124
+ #
125
+
126
+ def object(obj)
127
+ hash_entry(@allowed_objects, obj.object_id)
128
+ end
129
+
130
+ #
131
+ #Specifies the methods allowed for the instances of a class
132
+ #
133
+ #Example 1:
134
+ # privileges.instances_of(Array).allow :each # allow calls of methods named "each" over instances of Array
135
+ #
136
+ #Example 2:
137
+ # privileges.instances_of(Array).allow :select, map # allow calls of methods named "each" and "map" over instances of Array
138
+ #
139
+ #Example 3:
140
+ # privileges.instances_of(Hash).allow_all # allow any method call over instances of Hash
141
+
142
+ def instances_of(klass)
143
+ hash_entry(@allowed_instances, klass.object_id)
144
+ end
145
+
146
+ #
147
+ #Specifies the methods allowed for an implementation in specific class
148
+ #
149
+ #Example 1:
150
+ # privileges.methods_of(X).allow :foo
151
+ #
152
+ # ...
153
+ # class X
154
+ # def foo # allowed :)
155
+ # end
156
+ # end
157
+ #
158
+ # class Y < X
159
+ # def foo # disallowed
160
+ # end
161
+ # end
162
+ #
163
+ # X.new.foo # allowed
164
+ # Y.new.foo # disallowed: SecurityError
165
+ # ...
166
+ #
167
+ def methods_of(klass)
168
+ hash_entry(@allowed_klass_methods, klass.object_id)
169
+ end
170
+
171
+ #allow the execution of method named method_name whereever
172
+ #
173
+ #Example:
174
+ # privileges.allow_method(:foo)
175
+ #
176
+
177
+ def allow_method(method_name)
178
+ @allowed_methods << method_name.to_sym
179
+ self
180
+ end
181
+
182
+ def allow?(klass, recv, method_name, method_id)
183
+
184
+ m = nil
185
+ m = klass.instance_method(method_name) if method_name
186
+
187
+ begin
188
+ return true if @allowed_methods.include?(method_name)
189
+
190
+ tmp = @allowed_objects[recv.object_id]
191
+ if tmp
192
+ if tmp.allowed?(method_name)
193
+ @last_allowed = tmp
194
+ return true
195
+ end
196
+ end
197
+
198
+ if m
199
+ tmp = @allowed_klass_methods[m.owner.object_id]
200
+ if tmp
201
+ if tmp.allowed?(method_name)
202
+ @last_allowed = tmp
203
+ return true
204
+ end
205
+ end
206
+ end
207
+
208
+ if recv.instance_of? Class
209
+ last_class = recv
210
+
211
+ while true
212
+ tmp = @allowed_classes[last_class.object_id]
213
+ if tmp
214
+ if tmp.allowed?(method_name)
215
+ @last_allowed = tmp
216
+ return true
217
+ end
218
+ end
219
+ if last_class
220
+ break if last_class == Object
221
+ last_class = last_class.superclass
222
+ else
223
+ break
224
+ end
225
+ end
226
+ end
227
+
228
+ last_class = recv.class
229
+ while true
230
+ tmp = @allowed_kinds[last_class.object_id]
231
+ if tmp
232
+ if tmp.allowed?(method_name)
233
+ @last_allowed = tmp
234
+ return true
235
+ end
236
+ end
237
+ if last_class
238
+ break if last_class == Object
239
+ last_class = last_class.superclass
240
+ else
241
+ break
242
+ end
243
+ end
244
+
245
+ tmp = @allowed_instances[recv.class.object_id]
246
+ if tmp
247
+ if tmp.allowed?(method_name)
248
+ @last_allowed = tmp
249
+ return true
250
+ end
251
+ end
252
+
253
+ false
254
+ rescue Exception => e
255
+ print "ERROR: #{e}\n"
256
+ print e.backtrace.join("\n")
257
+ false
258
+ end
259
+ end
260
+
261
+ def xstr_allowed?
262
+ @xstr_allowed
263
+ end
264
+
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
271
+ end
272
+
273
+ def const_read_allowed?(varname)
274
+ @allowed_read_consts.include? varname
275
+ end
276
+
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
282
+ #
283
+ # Example:
284
+ #
285
+ # s = Sandbox.new
286
+ # priv = Privileges.new
287
+ #
288
+ # priv.allow_xstr
289
+ #
290
+ # s.run(priv, '
291
+ # %x[ls -l]
292
+ # ')
293
+ #
294
+ #
295
+ # Example 2:
296
+ #
297
+ # Sandbox.run('%x[ls -l]', Privileges.allow_xstr)
298
+ #
299
+ def allow_xstr
300
+ @xstr_allowed = true
301
+
302
+ self
303
+ end
304
+
305
+ # Enables the permissions needed to read one or more global variables
306
+ #
307
+ # Example:
308
+ #
309
+ # s = Sandbox.new
310
+ # priv = Privileges.new
311
+ #
312
+ # priv.allow_method :print
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
345
+ #
346
+ # s.run(priv, '
347
+ # $a = 9
348
+ # print "assigned 9 to $a\n"
349
+ # ')
350
+ #
351
+ # p $a
352
+ #
353
+ def allow_global_write( *varnames )
354
+ varnames.each do |varname|
355
+ @allowed_write_globals << varname.to_sym
356
+ end
357
+
358
+ self
359
+ end
360
+
361
+
362
+ # Enables the permissions needed to create or change one or more constants
363
+ #
364
+ # Example:
365
+ # s = Sandbox.new
366
+ # priv = Privileges.new
367
+ #
368
+ # priv.allow_method :print
369
+ # priv.allow_const_write "Object::A"
370
+ #
371
+ # s.run(priv, '
372
+ # print "assigned 8 to Object::A\n"
373
+ # A = 8
374
+ # ')
375
+ #
376
+ # p A
377
+
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
405
+ end
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
415
+ end
416
+
417
+ end
418
+