shikashi 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|