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.
- data/AUTHORS +3 -0
- data/CHANGELOG +1 -0
- data/README +199 -0
- data/Rakefile +48 -0
- data/TODO +0 -0
- data/examples/basic/example.rb +28 -0
- data/examples/basic/example1.rb +12 -0
- data/examples/basic/example2.rb +24 -0
- data/examples/basic/example3.rb +46 -0
- data/examples/basic/example4.rb +41 -0
- data/examples/basic/example5.rb +40 -0
- data/lib/shikashi.rb +23 -0
- data/lib/shikashi/pick_argument.rb +81 -0
- data/lib/shikashi/privileges.rb +294 -0
- data/lib/shikashi/privileges/classes.rb +28 -0
- data/lib/shikashi/privileges/exceptions.rb +29 -0
- data/lib/shikashi/privileges/singleton_methods.rb +28 -0
- data/lib/shikashi/sandbox.rb +381 -0
- metadata +81 -0
@@ -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
|
+
|