sandboxed 0.0.1

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/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :rubygems
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2011 Michael Klaus
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
data/lib/sandboxed.rb ADDED
@@ -0,0 +1,45 @@
1
+ require 'sandboxed/compat'
2
+ require 'sandboxed/proc'
3
+ require 'sandboxed/fiber'
4
+
5
+ module Kernel
6
+
7
+ # initialize worker fiber for sandboxed execution
8
+ SAFE_FIBER = Fiber.new do |msg|
9
+ while true
10
+ obj, m, args, block = msg
11
+ begin
12
+ msg = Fiber.yield(block ? obj.send(m, *args, &block) : obj.send(m, *args))
13
+ rescue Exception => e
14
+ msg = Fiber.yield(e)
15
+ end
16
+ end
17
+ end.freeze
18
+
19
+ def safe(*args, &block)
20
+ opts = args.last.is_a?(Hash) ? args.pop : {}
21
+ level = opts.delete(:level) || 4
22
+ ctx = opts.delete(:context) || eval('self', block.binding) # rebind to actual declaring object
23
+ args << opts unless opts.empty? # be nicer about passed in hashes
24
+
25
+ bound = block.bind(ctx) # TODO 1.8 compat is missing out here. How to?
26
+ Fiber.new do |l, c, b, a|
27
+ $SAFE = l
28
+ b.call *a
29
+ end.resume(level, ctx, bound, args)
30
+ end
31
+
32
+ def safe_method(*ms)
33
+ ms.each do |m|
34
+ safe_m = :"#{m}__safe_"
35
+ alias_method safe_m, m
36
+ define_method m do |*args, &block|
37
+ result = SAFE_FIBER.resume([self, safe_m, args, block])
38
+ result.is_a?(Exception) ? throw(result) : result # TODO find a better way
39
+ end
40
+ end
41
+ end
42
+
43
+ end
44
+
45
+
@@ -0,0 +1,5 @@
1
+ module Kernel
2
+ unless method_defined?(:untrust)
3
+ alias untrust taint
4
+ end
5
+ end
@@ -0,0 +1,73 @@
1
+ # Poor Man's Fiber (API compatible Thread based Fiber implementation for Ruby 1.8)
2
+ # (c) 2008 Aman Gupta (tmm1)
3
+
4
+ unless defined? Fiber
5
+ require 'thread'
6
+
7
+ class FiberError < StandardError; end
8
+
9
+ class Fiber
10
+ def initialize
11
+ raise ArgumentError, 'new Fiber requires a block' unless block_given?
12
+
13
+ @yield = Queue.new
14
+ @resume = Queue.new
15
+
16
+ @thread = Thread.new{ @yield.push [yield(*@resume.pop)] }
17
+ @thread.abort_on_exception = true
18
+ @thread[:fiber] = self
19
+ end
20
+ attr_reader :thread
21
+
22
+ def resume *args
23
+ raise FiberError, 'dead fiber called' unless @thread.alive?
24
+ @resume.push(args)
25
+ result = @yield.pop
26
+ result.size > 1 ? result : result.first
27
+ end
28
+
29
+ def yield *args
30
+ @yield.push(args)
31
+ result = @resume.pop
32
+ result.size > 1 ? result : result.first
33
+ end
34
+
35
+ def self.yield *args
36
+ raise FiberError, "can't yield from root fiber" unless fiber = Thread.current[:fiber]
37
+ fiber.yield(*args)
38
+ end
39
+
40
+ def self.current
41
+ Thread.current[:fiber] or raise FiberError, 'not inside a fiber'
42
+ end
43
+
44
+ def inspect
45
+ "#<#{self.class}:0x#{self.object_id.to_s(16)}>"
46
+ end
47
+ end
48
+ end
49
+
50
+ if __FILE__ == $0
51
+ f = Fiber.new{ puts 'hi'; p Fiber.yield(1); puts 'bye'; :done }
52
+ p f.resume
53
+ p f.resume(2)
54
+ end
55
+
56
+ __END__
57
+
58
+ $ ruby fbr.rb
59
+ hi
60
+ 1
61
+ 2
62
+ bye
63
+ :done
64
+
65
+ $ ruby1.9 fbr.rb
66
+ hi
67
+ 1
68
+ 2
69
+ bye
70
+ :done
71
+
72
+
73
+
@@ -0,0 +1,17 @@
1
+ unless Proc.method_defined?(:bind)
2
+
3
+ # copied from active_support proc.h to reduce dependencies
4
+ class Proc
5
+ def bind(object)
6
+ block, time = self, Time.now
7
+ class << object; self; end.class_eval do
8
+ method_name = "__bind_#{time.to_i}_#{time.usec}"
9
+ define_method(method_name, &block)
10
+ method = instance_method(method_name)
11
+ remove_method(method_name)
12
+ method
13
+ end.bind(object)
14
+ end
15
+ end
16
+ end
17
+
@@ -0,0 +1,3 @@
1
+ module Sandboxed
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,64 @@
1
+ require 'sandboxed'
2
+
3
+ class Ctx
4
+ attr_reader :log
5
+ def initialize
6
+ @log = []
7
+ end
8
+
9
+ def internal_log(text)
10
+ @log << text
11
+ end
12
+
13
+ def sandbox_log(text)
14
+ @log << text
15
+ end
16
+ safe_method :sandbox_log
17
+ end
18
+
19
+ describe Kernel do
20
+ describe "#safe" do
21
+ it "should use :level => 4 as default" do
22
+ safe { $SAFE }.should == 4
23
+ end
24
+ it "should execute safe operations" do
25
+ safe { 2 + 2 }.should == 4
26
+ end
27
+ it "should not execute unsafe operations" do
28
+ lambda{ safe{puts 'foo'} }.should raise_error SecurityError
29
+ end
30
+ it "should allow access depending on the :level" do
31
+ lambda{ safe(:level => 0){'foo'.taint} }.should_not raise_error SecurityError
32
+ end
33
+ it "should execute on the context object" do
34
+ safe(:context => 'foo'){ reverse }.should == 'oof'
35
+ end
36
+ it "should pass in safe local variables" do
37
+ arr = [].untrust
38
+ safe(arr){ |a| a << 'foo'}.should == ['foo']
39
+ arr.should == ['foo']
40
+ end
41
+ it "should pass in unsafe local variables" do
42
+ arr = []
43
+ lambda{ safe(arr){|a| a << 'foo'} }.should raise_error SecurityError
44
+ arr.should == []
45
+ end
46
+ it "should pass in hash local variables and options" do
47
+ safe(:from => 2, :len => 3, :context => 'foobar'){ |h| self[h[:from],h[:len]] }.should == 'oba'
48
+ end
49
+ end
50
+
51
+ describe "#safe_method" do
52
+ before(:each) do
53
+ @ctx = Ctx.new
54
+ end
55
+
56
+ it "should not execute unsafe methods" do
57
+ lambda{ safe(:context => @ctx){internal_log 'foo'} }.should raise_error SecurityError
58
+ end
59
+ it "should execute safe methods" do
60
+ safe(:context => @ctx){sandbox_log 'foo'}.should == ['foo']
61
+ end
62
+ end
63
+
64
+ end
metadata ADDED
@@ -0,0 +1,86 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sandboxed
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Michael Klaus
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-04-11 00:00:00 +02:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rspec
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ type: :development
32
+ version_requirements: *id001
33
+ description: Execute code blocks in a $SAFE environment.
34
+ email:
35
+ - Michael.Klaus@gmx.net
36
+ executables: []
37
+
38
+ extensions: []
39
+
40
+ extra_rdoc_files: []
41
+
42
+ files:
43
+ - lib/sandboxed.rb
44
+ - lib/sandboxed/compat.rb
45
+ - lib/sandboxed/version.rb
46
+ - lib/sandboxed/proc.rb
47
+ - lib/sandboxed/fiber.rb
48
+ - spec/sandboxed_spec.rb
49
+ - LICENSE
50
+ - Gemfile
51
+ has_rdoc: true
52
+ homepage: http://github.com/QaDeS/sandboxed
53
+ licenses: []
54
+
55
+ post_install_message:
56
+ rdoc_options: []
57
+
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ segments:
66
+ - 0
67
+ version: "0"
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ segments:
74
+ - 1
75
+ - 3
76
+ - 6
77
+ version: 1.3.6
78
+ requirements: []
79
+
80
+ rubyforge_project: sandboxed
81
+ rubygems_version: 1.3.7
82
+ signing_key:
83
+ specification_version: 3
84
+ summary: A ruby execution sandbox
85
+ test_files: []
86
+