sandboxed 0.0.1 → 0.0.2

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.
@@ -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