shikashi 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,28 @@
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 Shikashi
22
+ class Privileges
23
+ #Define the permissions needed to define singleton methods within the sandbox
24
+ def allow_singleton_methods
25
+ allow_method :singleton_method_added
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,381 @@
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
+
22
+ require "rallhook"
23
+ require "shikashi/privileges"
24
+ require "shikashi/pick_argument"
25
+
26
+ module Shikashi
27
+
28
+ class << self
29
+ attr_accessor :global_binding
30
+ end
31
+
32
+
33
+ #The sandbox class run the sandbox, because of internal behaviour only can be use one instance
34
+ #of sandbox by thread (each different thread may have its own sandbox running in the same time)
35
+ #
36
+ #= Example
37
+ #
38
+ # require "rubygems"
39
+ # require "shikashi"
40
+ #
41
+ # include Shikashi
42
+ #
43
+ # s = Sandbox.new
44
+ # priv = Privileges.new
45
+ # priv.allow_method :print
46
+ #
47
+ # s.run(priv, 'print "hello world\n"')
48
+ #
49
+ class Sandbox
50
+
51
+ #array of privileges of restricted code within sandbox
52
+ #
53
+ #Example
54
+ # sandbox.privileges[source].allow_method :raise
55
+ #
56
+ attr_reader :privileges
57
+ #Binding of execution, the default is a binding in a global context allowing the definition of module of classes
58
+ attr_reader :chain
59
+
60
+ #
61
+ # Generate a random source file name for the sandbox, used internally
62
+ #
63
+
64
+ def generate_id
65
+ "sandbox-#{rand(1000000)}"
66
+ end
67
+
68
+ def initialize
69
+ @privileges = Hash.new
70
+ @chain = Hash.new
71
+ @redirect_hash = Hash.new
72
+ end
73
+
74
+ # add a chain of sources, used internally
75
+ def add_source_chain(outer, inner)
76
+ @chain[inner] = outer
77
+ end
78
+
79
+
80
+ #Base class to define redirections of methods called in the sandbox
81
+ #
82
+ #= Example 1
83
+ #Basic redirection
84
+ #
85
+ # require "rubygems"
86
+ # require "shikashi"
87
+ #
88
+ # class TestWrapper < Shikashi::Sandbox::MethodWrapper
89
+ # def call(*args)
90
+ # print "called foo from source: #{source}, arguments: #{args.inspect} \n"
91
+ # original_call(*args)
92
+ # end
93
+ # end
94
+ #
95
+ #
96
+ # class X
97
+ # def foo
98
+ # print "original foo\n"
99
+ # end
100
+ # end
101
+ #
102
+ #
103
+ # s = Shikashi::Sandbox.new
104
+ # perm = Shikashi::Privileges.new
105
+ #
106
+ # perm.object(X).allow :new
107
+ # perm.instances_of(X).allow :foo
108
+ #
109
+ # # redirect calls to foo to TestWrapper
110
+ # perm.instances_of(X).redirect :foo, TestWrapper
111
+ #
112
+ # s.run(perm,"X.new.foo")
113
+ #
114
+ #= Example 2
115
+ #Proper block handling on redirection wrapper
116
+ #
117
+ # require "rubygems"
118
+ # require "shikashi"
119
+ #
120
+ # class TestWrapper < Shikashi::Sandbox::MethodWrapper
121
+ # def call(*args)
122
+ # print "called each from source: #{source}\n"
123
+ # if block_given?
124
+ # original_call(*args) do |*x|
125
+ # yield(*x)
126
+ # end
127
+ # else
128
+ # original_call(*args)
129
+ # end
130
+ # end
131
+ # end
132
+ #
133
+ # s = Shikashi::Sandbox.new
134
+ # perm = Shikashi::Privileges.new
135
+ # perm.instances_of(Array).allow :each
136
+ # perm.instances_of(Enumerable::Enumerator).allow :each
137
+ # perm.allow_method :print
138
+ #
139
+ # s.run perm, '
140
+ # array = [1,2,3]
141
+ #
142
+ # array.each do |x|
143
+ # print x,"\n"
144
+ # end
145
+ #
146
+ # enum = array.each
147
+ # enum.each do |x|
148
+ # print x,"\n"
149
+ # end
150
+ # '
151
+ class MethodWrapper < RallHook::Helper::MethodWrapper
152
+ attr_accessor :sandbox
153
+ attr_accessor :privileges
154
+ attr_accessor :source
155
+
156
+ def self.redirect_handler(klass,recv,method_name,method_id,sandbox)
157
+ wrap = self.new
158
+ wrap.klass = klass
159
+ wrap.recv = recv
160
+ wrap.method_name = method_name
161
+ wrap.method_id = method_id
162
+ wrap.sandbox = sandbox
163
+
164
+ if block_given?
165
+ yield(wrap)
166
+ end
167
+
168
+ return wrap.redirect_with_unhook(:call_with_rehook)
169
+ end
170
+ end
171
+
172
+ # Used internally
173
+ class InheritedWrapper < MethodWrapper
174
+ def call(*args)
175
+ subclass = args.first
176
+ privileges.object(subclass).allow :new
177
+ privileges.instances_of(subclass).allow :initialize
178
+ original_call(*args)
179
+ end
180
+ end
181
+
182
+ # Used internally
183
+ class DummyWrapper < MethodWrapper
184
+ def call(*args)
185
+ if block_given?
186
+ original_call(*args) do |*x|
187
+ yield(*x)
188
+ end
189
+ else
190
+ original_call(*args)
191
+ end
192
+ end
193
+ end
194
+
195
+ class RallhookHandler < RallHook::HookHandler
196
+ attr_accessor :sandbox
197
+ attr_accessor :redirect
198
+
199
+ def handle_method(klass, recv, method_name, method_id)
200
+ source = nil
201
+ if method_name
202
+
203
+ source = caller.first.split(":").first
204
+ dest_source = klass.shadow.instance_method(method_name).body.file
205
+
206
+ privileges = nil
207
+ if source != dest_source then
208
+ privileges = sandbox.privileges[source]
209
+ if privileges then
210
+ privileges = privileges.dup
211
+ loop_source = source
212
+ loop_privileges = privileges
213
+
214
+ while loop_privileges and loop_source != dest_source
215
+ unless loop_privileges.allow?(klass,recv,method_name,method_id)
216
+ raise SecurityError.new("Cannot invoke method #{method_name} on object of class #{klass}")
217
+ end
218
+
219
+ loop_privileges = nil
220
+ loop_source = sandbox.chain[loop_source]
221
+
222
+ if dest_source then
223
+ loop_privileges = sandbox.privileges[loop_source]
224
+ else
225
+ loop_privileges = nil
226
+ end
227
+
228
+ end
229
+ end
230
+ end
231
+
232
+ if method_name == :inherited and recv.instance_of? Class
233
+ mw = InheritedWrapper.redirect_handler(klass,recv,method_name,method_id,sandbox)
234
+ mw.recv.privileges = privileges
235
+ return mw
236
+ end
237
+
238
+ return nil if method_name == :instance_eval
239
+ return nil if method_name == :binding
240
+
241
+ if method_name
242
+ wclass = @redirect[method_name.to_sym]
243
+ if wclass then
244
+ return wclass.redirect_handler(klass,recv,method_name,method_id,sandbox)
245
+ end
246
+ end
247
+
248
+ if dest_source == ""
249
+ return DummyWrapper.redirect_handler(klass,recv,method_name,method_id,sandbox)
250
+ end
251
+
252
+ end
253
+
254
+
255
+ if privileges
256
+ privileges.handle_redirection(klass,recv,method_id,sandbox) do |mh|
257
+ mh.privileges = privileges
258
+ mh.source = source
259
+ end
260
+ end
261
+
262
+ end # if
263
+ end # Class
264
+
265
+ #Run the code in sandbox with the given privileges, also run privileged code in the sandbox context for
266
+ #execution of classes and methods defined in the sandbox from outside the sandbox if a block is passed
267
+ # (see examples)
268
+ #
269
+ #call-seq: run(arguments)
270
+ #
271
+ #Arguments
272
+ #
273
+ # :code Mandatory argument of class String with the code to execute restricted in the sandbox
274
+ # :privileges Optional argument of class Shikashi::Sandbox::Privileges to indicate the restrictions of the
275
+ # code executed in the sandbox. The default is an empty Privileges (absolutly no permission)
276
+ # Must be of class Privileges or passed as hash_key (:privileges => privileges)
277
+ # :binding Optional argument with the binding object of the context where the code is to be executed
278
+ # The default is a binding in the global context
279
+ # :source Optional argument to indicate the "source name", (visible in the backtraces). Only can
280
+ # be specified as hash parameter
281
+ #
282
+ #
283
+ #The arguments can be passed in any order and using hash notation or not, examples:
284
+ #
285
+ # sandbox.run code, privileges
286
+ # sandbox.run code, :privileges => privileges
287
+ # sandbox.run :code => code, :privileges => privileges
288
+ # sandbox.run code, privileges, binding
289
+ # sandbox.run binding, code, privileges
290
+ # #etc
291
+ # sandbox.run binding, code, privileges, :source => source
292
+ # sandbox.run binding, :code => code, :privileges => privileges, :source => source
293
+ #
294
+ #Example:
295
+ #
296
+ # require "rubygems"
297
+ # require "shikashi"
298
+ #
299
+ # include Shikashi
300
+ #
301
+ # sandbox = Sandbox.new
302
+ # privileges = Privileges.new
303
+ # privileges.allow_method :print
304
+ # sandbox.run('print "hello world\n"', :privileges => privileges)
305
+ #
306
+ #Example 2:
307
+ # require "rubygems"
308
+ # require "shikashi"
309
+ #
310
+ # include Shikashi
311
+ #
312
+ # sandbox = Sandbox.new
313
+ # privileges = Privileges.new
314
+ # privileges.allow_method :print
315
+ # privileges.allow_method :singleton_method_added
316
+ #
317
+ # sandbox.run('
318
+ # def self.foo
319
+ # print "hello world\n"
320
+ # end
321
+ # ', :privileges => privileges)
322
+ #
323
+ # #outside of this block, the method foo defined in the sandbox are invisible
324
+ # sandbox.run do
325
+ # self.foo
326
+ # end
327
+ #
328
+ #
329
+
330
+ def run(*args)
331
+
332
+ handler = RallhookHandler.new
333
+ handler.redirect = @redirect_hash
334
+ handler.sandbox = self
335
+
336
+ if block_given?
337
+ handler.hook do
338
+ yield
339
+ end
340
+ else
341
+
342
+ privileges_ = args.pick(Privileges,:privileges) do Privileges.new end
343
+ code = args.pick(String,:code)
344
+ binding_ = args.pick(Binding,:binding) do Shikashi.global_binding end
345
+ source = args.pick(:source) do generate_id end
346
+
347
+ self.privileges[source] = privileges_
348
+
349
+ handler.hook do
350
+ eval(code, binding_, source)
351
+ end
352
+ end
353
+ end
354
+
355
+
356
+ #redirects a method with given name to a wrapper of the given class
357
+ #example:
358
+ # class PrintWrapper < Shikashi::Sandbox::MethodWrapper
359
+ # def call(*args)
360
+ # print "invoked print\n"
361
+ # original_call(*args)
362
+ # end
363
+ # end
364
+ #
365
+ # sandbox.redirect(:print, PrintWrapper)
366
+ # sandbox.redirect(:print, :wrapper_class => PrintWrapper)
367
+ # sandbox.redirect(:method_name => :print, :wrapper_class => PrintWrapper)
368
+ #
369
+
370
+ def redirect(*args)
371
+ mname = args.pick(Symbol, :method_name)
372
+ wclass = args.pick(Class, :wrapper_class)
373
+ @redirect_hash[mname] = wclass
374
+ end
375
+
376
+ end
377
+ end
378
+
379
+ Shikashi.global_binding = binding()
380
+
381
+
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: shikashi
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Dario Seminara
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-06-20 00:00:00 -03:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rallhook
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.7.0
24
+ version:
25
+ description:
26
+ email: robertodarioseminara@gmail.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - README
33
+ files:
34
+ - examples/basic/example1.rb
35
+ - examples/basic/example3.rb
36
+ - examples/basic/example5.rb
37
+ - examples/basic/example4.rb
38
+ - examples/basic/example.rb
39
+ - examples/basic/example2.rb
40
+ - lib/shikashi.rb
41
+ - lib/shikashi/pick_argument.rb
42
+ - lib/shikashi/sandbox.rb
43
+ - lib/shikashi/privileges/exceptions.rb
44
+ - lib/shikashi/privileges/singleton_methods.rb
45
+ - lib/shikashi/privileges/classes.rb
46
+ - lib/shikashi/privileges.rb
47
+ - AUTHORS
48
+ - CHANGELOG
49
+ - README
50
+ - Rakefile
51
+ - TODO
52
+ has_rdoc: true
53
+ homepage: http://github.com/tario/shikashi
54
+ licenses: []
55
+
56
+ post_install_message:
57
+ rdoc_options: []
58
+
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: "0"
66
+ version:
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: "0"
72
+ version:
73
+ requirements: []
74
+
75
+ rubyforge_project:
76
+ rubygems_version: 1.3.5
77
+ signing_key:
78
+ specification_version: 3
79
+ summary: shikashi is a ruby sandbox that permits the execution of "unprivileged" scripts by defining the permitted methods and constants the scripts can invoke (I.E., the script cannot use the File class or a RoR Model Class unless that permission is specified) "well done version" of ruby-arena-sanbox based on rallhook
80
+ test_files: []
81
+