shikashi 0.1.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.
@@ -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
+