sandboxed 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,8 +1,87 @@
1
+ if RUBY_PLATFORM == 'jruby'
2
+ warn "Sandboxed is not supported on JRuby"
3
+ end
4
+
1
5
  require 'sandboxed/compat'
2
6
  require 'sandboxed/proc'
3
7
  require 'sandboxed/fiber'
4
8
 
5
- module Kernel
9
+ module Sandboxed
10
+ module Modes
11
+ class << self
12
+ def ctx_only(level, ctx, args, &block)
13
+ proc {
14
+ $SAFE=level
15
+ (ctx || eval("self", block.binding)).instance_exec(*args, &block)
16
+ }.call
17
+ end
18
+
19
+ def overlay(level, ctx, args, &block)
20
+ class << eval("self", block.binding)
21
+ include ContextHolder
22
+ end if ctx
23
+
24
+ proc {
25
+ $SAFE = level
26
+ yield *args
27
+ }.call
28
+ end
29
+ end
30
+ end
31
+ MODES = {:overlay => '1.8.6', :ctx_only => '1.8.7'}.reject{|m, v| v > RUBY_VERSION}
32
+ MODE_PREFERENCE = [:overlay, :ctx_only] & MODES.keys
33
+
34
+ class << self
35
+
36
+ def default_mode
37
+ @default_mode ||= MODE_PREFERENCE.first
38
+ end
39
+ def default_mode=(mode)
40
+ @default_mode = check_mode(mode)
41
+ end
42
+
43
+ def safe(*args, &block)
44
+ contexts = Thread.current[:__contexts_] ||= []
45
+ opts = args.last.is_a?(Hash) ? args.pop : {}
46
+
47
+ mode = opts.delete(:mode) || default_mode
48
+ check_mode(mode)
49
+
50
+ level = opts.delete(:level) || 4
51
+ ctx = opts.delete(:context)
52
+ args << opts unless opts.empty? # be nicer about passed in hashes
53
+
54
+ contexts << ctx
55
+ result = Modes.send(mode, level, ctx, args, &block)
56
+ contexts.pop if ctx
57
+
58
+ result
59
+ end
60
+
61
+ def safe_method(mdl, *ms)
62
+ code = ms.map do |m|
63
+ safe_m = "#{m}__safe_"
64
+ <<-RUBY
65
+ alias #{safe_m} #{m}
66
+ def #{m}(*args, &block)
67
+ result = SAFE_FIBER.resume([self, :#{safe_m}, args, block])
68
+ result.is_a?(Exception) ? throw(result) : result # TODO find a better way
69
+ end
70
+ RUBY
71
+ end.join("\n")
72
+ mdl.class_eval code
73
+ end
74
+
75
+ private
76
+ def check_mode(m)
77
+ mode = m.to_sym
78
+ return mode if MODES.member?(mode)
79
+ raise ArgumentError, "Sandbox mode '#{mode}' is not defined; must be one of #{MODES.keys.map.join(', ')}"
80
+ end
81
+
82
+ end
83
+
84
+ private
6
85
 
7
86
  # initialize worker fiber for sandboxed execution
8
87
  SAFE_FIBER = Fiber.new do |msg|
@@ -16,30 +95,28 @@ module Kernel
16
95
  end
17
96
  end.freeze
18
97
 
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
98
+ module ContextHolder
99
+ def method_missing(name, *args, &block)
100
+ if ctx = Thread.current[:__contexts_].last
101
+ if ctx.respond_to?(name)
102
+ return block ? ctx.send(name, *args, &block) : ctx.send(name, *args)
103
+ end
39
104
  end
105
+ super
40
106
  end
41
107
  end
108
+ end
42
109
 
110
+ if mode = ENV['SANDBOX']
111
+ Sandboxed.default_mode = mode
43
112
  end
44
113
 
114
+ module Kernel
115
+ def safe(*args, &block)
116
+ Sandboxed.safe *args, &block
117
+ end
118
+ def safe_method(*names)
119
+ Sandboxed.safe_method self, *names
120
+ end
121
+ end
45
122
 
@@ -1,26 +1,23 @@
1
1
  # Poor Man's Fiber (API compatible Thread based Fiber implementation for Ruby 1.8)
2
- # (c) 2008 Aman Gupta (tmm1)
2
+ # Based on https://gist.github.com/4631 (c) 2008 Aman Gupta (tmm1)
3
3
 
4
4
  unless defined? Fiber
5
- require 'thread'
5
+ require 'thread'
6
6
 
7
7
  class FiberError < StandardError; end
8
8
 
9
- class Fiber
9
+ class Fiber < Thread
10
10
  def initialize
11
11
  raise ArgumentError, 'new Fiber requires a block' unless block_given?
12
-
13
12
  @yield = Queue.new
14
13
  @resume = Queue.new
15
14
 
16
- @thread = Thread.new{ @yield.push [yield(*@resume.pop)] }
17
- @thread.abort_on_exception = true
18
- @thread[:fiber] = self
15
+ super{ @yield.push [yield(*@resume.pop)] }
16
+ abort_on_exception = true
19
17
  end
20
- attr_reader :thread
21
18
 
22
19
  def resume *args
23
- raise FiberError, 'dead fiber called' unless @thread.alive?
20
+ raise FiberError, 'dead fiber called' unless alive?
24
21
  @resume.push(args)
25
22
  result = @yield.pop
26
23
  result.size > 1 ? result : result.first
@@ -33,41 +30,13 @@ unless defined? Fiber
33
30
  end
34
31
 
35
32
  def self.yield *args
36
- raise FiberError, "can't yield from root fiber" unless fiber = Thread.current[:fiber]
33
+ raise FiberError, "can't yield from root fiber" unless fiber = Thread.current
37
34
  fiber.yield(*args)
38
35
  end
39
36
 
40
- def self.current
41
- Thread.current[:fiber] or raise FiberError, 'not inside a fiber'
42
- end
43
-
44
37
  def inspect
45
38
  "#<#{self.class}:0x#{self.object_id.to_s(16)}>"
46
39
  end
47
40
  end
48
41
  end
49
42
 
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
-
@@ -1,6 +1,6 @@
1
1
  unless Proc.method_defined?(:bind)
2
2
 
3
- # copied from active_support proc.h to reduce dependencies
3
+ # copied from active_support proc.rb to reduce dependencies
4
4
  class Proc
5
5
  def bind(object)
6
6
  block, time = self, Time.now
@@ -1,3 +1,3 @@
1
1
  module Sandboxed
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -25,10 +25,10 @@ describe Kernel do
25
25
  safe { 2 + 2 }.should == 4
26
26
  end
27
27
  it "should not execute unsafe operations" do
28
- lambda{ safe{puts 'foo'} }.should raise_error SecurityError
28
+ lambda{ safe{puts 'foo'} }.should raise_error(SecurityError)
29
29
  end
30
30
  it "should allow access depending on the :level" do
31
- lambda{ safe(:level => 0){'foo'.taint} }.should_not raise_error SecurityError
31
+ lambda{ safe(:level => 0){'foo'.taint} }.should_not raise_error(SecurityError)
32
32
  end
33
33
  it "should execute on the context object" do
34
34
  safe(:context => 'foo'){ reverse }.should == 'oof'
@@ -40,7 +40,7 @@ describe Kernel do
40
40
  end
41
41
  it "should pass in unsafe local variables" do
42
42
  arr = []
43
- lambda{ safe(arr){|a| a << 'foo'} }.should raise_error SecurityError
43
+ lambda{ safe(arr){|a| a << 'foo'} }.should raise_error(SecurityError)
44
44
  arr.should == []
45
45
  end
46
46
  it "should pass in hash local variables and options" do
@@ -54,7 +54,7 @@ describe Kernel do
54
54
  end
55
55
 
56
56
  it "should not execute unsafe methods" do
57
- lambda{ safe(:context => @ctx){internal_log 'foo'} }.should raise_error SecurityError
57
+ lambda{ safe(:context => @ctx){internal_log 'foo'} }.should raise_error(SecurityError)
58
58
  end
59
59
  it "should execute safe methods" do
60
60
  safe(:context => @ctx){sandbox_log 'foo'}.should == ['foo']
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 0
8
- - 1
9
- version: 0.0.1
8
+ - 2
9
+ version: 0.0.2
10
10
  platform: ruby
11
11
  authors:
12
12
  - Michael Klaus
@@ -14,14 +14,13 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2011-04-11 00:00:00 +02:00
17
+ date: 2011-04-13 00:00:00 +02:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: rspec
22
22
  prerelease: false
23
23
  requirement: &id001 !ruby/object:Gem::Requirement
24
- none: false
25
24
  requirements:
26
25
  - - ">="
27
26
  - !ruby/object:Gem::Version
@@ -58,7 +57,6 @@ rdoc_options: []
58
57
  require_paths:
59
58
  - lib
60
59
  required_ruby_version: !ruby/object:Gem::Requirement
61
- none: false
62
60
  requirements:
63
61
  - - ">="
64
62
  - !ruby/object:Gem::Version
@@ -66,7 +64,6 @@ required_ruby_version: !ruby/object:Gem::Requirement
66
64
  - 0
67
65
  version: "0"
68
66
  required_rubygems_version: !ruby/object:Gem::Requirement
69
- none: false
70
67
  requirements:
71
68
  - - ">="
72
69
  - !ruby/object:Gem::Version
@@ -78,7 +75,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
78
75
  requirements: []
79
76
 
80
77
  rubyforge_project: sandboxed
81
- rubygems_version: 1.3.7
78
+ rubygems_version: 1.3.6
82
79
  signing_key:
83
80
  specification_version: 3
84
81
  summary: A ruby execution sandbox