sandboxed 0.0.1

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