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.
- data/lib/sandboxed.rb +98 -21
- data/lib/sandboxed/fiber.rb +7 -38
- data/lib/sandboxed/proc.rb +1 -1
- data/lib/sandboxed/version.rb +1 -1
- data/spec/sandboxed_spec.rb +4 -4
- metadata +4 -7
data/lib/sandboxed.rb
CHANGED
@@ -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
|
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
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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
|
|
data/lib/sandboxed/fiber.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
17
|
-
|
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
|
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
|
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
|
-
|
data/lib/sandboxed/proc.rb
CHANGED
data/lib/sandboxed/version.rb
CHANGED
data/spec/sandboxed_spec.rb
CHANGED
@@ -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
|
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
|
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
|
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
|
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
|
-
-
|
9
|
-
version: 0.0.
|
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-
|
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.
|
78
|
+
rubygems_version: 1.3.6
|
82
79
|
signing_key:
|
83
80
|
specification_version: 3
|
84
81
|
summary: A ruby execution sandbox
|