jruby_sandbox 0.1.1-java → 0.1.2-java
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.lock +1 -1
- data/README.md +16 -2
- data/lib/sandbox/version.rb +1 -1
- data/lib/sandbox.rb +20 -0
- data/spec/exploits_spec.rb +123 -0
- data/spec/sandbox_spec.rb +33 -11
- metadata +14 -12
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -56,10 +56,24 @@ Sandbox::Safe exposes an `#activate!` method which will lock down the sandbox, r
|
|
56
56
|
|
57
57
|
Sandbox::Safe works by whitelisting methods to keep, and removing the rest. Checkout sandbox.rb for which methods are kept.
|
58
58
|
|
59
|
+
Sandbox::Safe.activate! will also isolate the sandbox environment from the filesystem using FakeFS.
|
60
|
+
|
61
|
+
>> require 'sandbox'
|
62
|
+
=> true
|
63
|
+
>> s = Sandbox.safe
|
64
|
+
=> #<Sandbox::Safe:0x3fdb8a73>
|
65
|
+
>> s.eval('Dir["/"]')
|
66
|
+
=> ["/"]
|
67
|
+
>> s.eval('Dir["/*"]')
|
68
|
+
=> ["/Applications", "/bin", "/cores", "/dev", etc.]
|
69
|
+
> s.activate!
|
70
|
+
>> s.eval('Dir["/*"]')
|
71
|
+
=> []
|
72
|
+
> Dir['/*']
|
73
|
+
=> ["/Applications", "/bin", "/cores", "/dev", etc.]
|
74
|
+
|
59
75
|
## Known Issues / TODOs
|
60
76
|
|
61
|
-
* It would be a good idea to integrate something like FakeFS to stub
|
62
|
-
out the filesystem in the sandbox.
|
63
77
|
* There is currently no timeout support, so it's possible for a
|
64
78
|
sandbox to loop indefinitely and block the host interpreter.
|
65
79
|
|
data/lib/sandbox/version.rb
CHANGED
data/lib/sandbox.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'sandbox/sandbox'
|
2
2
|
require 'sandbox/version'
|
3
3
|
require 'fakefs/safe'
|
4
|
+
require 'timeout'
|
4
5
|
|
5
6
|
module Sandbox
|
6
7
|
PRELUDE = File.expand_path('../sandbox/prelude.rb', __FILE__).freeze # :nodoc:
|
@@ -22,6 +23,7 @@ module Sandbox
|
|
22
23
|
keep_singleton_methods(:Kernel, KERNEL_S_METHODS)
|
23
24
|
keep_singleton_methods(:Symbol, SYMBOL_S_METHODS)
|
24
25
|
keep_singleton_methods(:String, STRING_S_METHODS)
|
26
|
+
keep_singleton_methods(:IO, IO_S_METHODS)
|
25
27
|
|
26
28
|
keep_methods(:Kernel, KERNEL_METHODS)
|
27
29
|
keep_methods(:NilClass, NILCLASS_METHODS)
|
@@ -63,6 +65,24 @@ module Sandbox
|
|
63
65
|
|
64
66
|
FakeFS::FileSystem.clear
|
65
67
|
end
|
68
|
+
|
69
|
+
def eval_with_timeout(code, timeout=10)
|
70
|
+
require 'timeout'
|
71
|
+
|
72
|
+
timeout_code = <<-RUBY
|
73
|
+
Timeout.timeout(#{timeout}) do
|
74
|
+
#{code}
|
75
|
+
end
|
76
|
+
RUBY
|
77
|
+
|
78
|
+
eval timeout_code
|
79
|
+
end
|
80
|
+
|
81
|
+
IO_S_METHODS = %w[
|
82
|
+
new
|
83
|
+
foreach
|
84
|
+
open
|
85
|
+
]
|
66
86
|
|
67
87
|
KERNEL_S_METHODS = %w[
|
68
88
|
Array
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'sandbox'
|
3
|
+
|
4
|
+
class OutsideFoo
|
5
|
+
def self.bar; 'bar'; end
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "Sandbox exploits" do
|
9
|
+
subject { Sandbox.safe }
|
10
|
+
|
11
|
+
before(:each) do
|
12
|
+
subject.activate!
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should not allow running any commands or reading files using IO" do
|
16
|
+
expect {
|
17
|
+
subject.eval 'f=IO.popen("uname"); f.readlines; f.close'
|
18
|
+
}.to raise_error(Sandbox::SandboxException, /NoMethodError/)
|
19
|
+
|
20
|
+
expect {
|
21
|
+
subject.eval 'IO.binread("/etc/passwd")'
|
22
|
+
}.to raise_error(Sandbox::SandboxException, /NoMethodError/)
|
23
|
+
|
24
|
+
expect {
|
25
|
+
subject.eval 'IO.read("/etc/passwd")'
|
26
|
+
}.to raise_error(Sandbox::SandboxException, /NoMethodError/)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should not pass through methods added to Kernel" do
|
30
|
+
k = subject.eval("Kernel")
|
31
|
+
def k.crack
|
32
|
+
open("/etc/passwd")
|
33
|
+
end
|
34
|
+
|
35
|
+
Kernel.should respond_to(:crack)
|
36
|
+
subject.eval("Kernel.respond_to?(:crack)").should == false
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should not allow calling fork on Kernel, even through eval" do
|
40
|
+
subject.eval("eval('Kernel').respond_to?(:fork)").should == false
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should not get access to outside the box objects by using eval and TOPLEVEL_BINDING" do
|
44
|
+
expect {
|
45
|
+
subject.eval(%{eval('OutsideFoo.bar', TOPLEVEL_BINDING)})
|
46
|
+
}.to raise_error(Sandbox::SandboxException, /NameError/)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should not get access to the outside eval through a ref'd object" do
|
50
|
+
subject.ref OutsideFoo
|
51
|
+
subject.eval "obj = OutsideFoo.new"
|
52
|
+
subject.eval("(obj.methods.grep /^eval/).empty?").should == true
|
53
|
+
subject.eval("obj.respond_to?(:eval)").should == false
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should not allow file access, even through a ref hack" do
|
57
|
+
unsafe_open = %{File.open('/etc/passwd').read}
|
58
|
+
|
59
|
+
expect {
|
60
|
+
subject.eval(unsafe_open)
|
61
|
+
}.to raise_error(Sandbox::SandboxException)
|
62
|
+
|
63
|
+
subject.ref OutsideFoo
|
64
|
+
subject.eval "obj = OutsideFoo.new"
|
65
|
+
|
66
|
+
unsafe_open_hack = %{obj.eval "#{unsafe_open}"}
|
67
|
+
|
68
|
+
pending "gotta figure out how to lock down ref'd objects eval" do
|
69
|
+
expect {
|
70
|
+
subject.eval(unsafe_open_hack)
|
71
|
+
}.to raise_error(Sandbox::SandboxException)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should have safe globals" do
|
76
|
+
subject.eval('$0').should == '(sandbox)'
|
77
|
+
/(.)(.)(.)/.match("abc")
|
78
|
+
subject.eval("$TEST = 'TEST'; $TEST").should == "TEST"
|
79
|
+
subject.eval("/(.)(.)(.)/.match('def'); $2").should == "e"
|
80
|
+
$2.should == "b"
|
81
|
+
subject.eval("$TEST").should == "TEST"
|
82
|
+
subject.eval("$2").should == "e"
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should not keep Kernel.fork" do
|
86
|
+
expect {
|
87
|
+
subject.eval("Kernel.fork")
|
88
|
+
}.to raise_error(Sandbox::SandboxException)
|
89
|
+
|
90
|
+
expect {
|
91
|
+
subject.eval("fork")
|
92
|
+
}.to raise_error(Sandbox::SandboxException)
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should not allow the sandbox to get back to Kernel through ancestors" do
|
96
|
+
subject.eval('$0.class.ancestors[3].respond_to?(:fork)').should == false
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should not pass through block scope" do
|
100
|
+
1.times do |i|
|
101
|
+
subject.eval('local_variables').should == []
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should not allow exploits through match data" do
|
106
|
+
subject.eval("begin; /(.+)/.match('FreakyFreaky').instance_eval { open('/etc/passwd') }; rescue NameError; :NameError; end").should == :NameError
|
107
|
+
|
108
|
+
subject.eval("begin;(begin;Regexp.new('(');rescue e;e;end).instance_eval{ open('/etc/passwd') }; rescue NameError; :NameError; end").should == :NameError
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should not be able to access outside box Kernel through exceptions" do
|
112
|
+
exception_code = <<-RUBY
|
113
|
+
begin
|
114
|
+
raise
|
115
|
+
rescue => e
|
116
|
+
obj = e
|
117
|
+
end
|
118
|
+
RUBY
|
119
|
+
subject.eval(exception_code)
|
120
|
+
|
121
|
+
subject.eval('obj.class.ancestors[4].respond_to?(:fork)').should == false
|
122
|
+
end
|
123
|
+
end
|
data/spec/sandbox_spec.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'rspec'
|
2
2
|
require 'sandbox'
|
3
|
+
require 'timeout'
|
3
4
|
|
4
5
|
describe Sandbox do
|
5
6
|
after(:each) do
|
@@ -56,17 +57,6 @@ describe Sandbox do
|
|
56
57
|
subject.eval(%{FileUtils.cp('/bar.txt', '/baz.txt')})
|
57
58
|
}.to_not raise_error(Sandbox::SandboxException, /NoMethodError/)
|
58
59
|
end
|
59
|
-
|
60
|
-
it "sandboxes global variables" do
|
61
|
-
subject.eval('$0').should == '(sandbox)'
|
62
|
-
/(.)(.)(.)/.match('abc')
|
63
|
-
subject.eval('$TEST = "TEST"; $TEST').should == 'TEST'
|
64
|
-
subject.eval('/(.)(.)(.)/.match("def"); $2').should == 'e'
|
65
|
-
$2.should == 'b'
|
66
|
-
subject.eval('$TEST').should == 'TEST'
|
67
|
-
subject.eval('$2').should == 'e'
|
68
|
-
/(.)(.)(.)/.match('ghi')
|
69
|
-
end
|
70
60
|
end
|
71
61
|
|
72
62
|
describe ".current" do
|
@@ -82,6 +72,38 @@ describe Sandbox do
|
|
82
72
|
end
|
83
73
|
end
|
84
74
|
end
|
75
|
+
|
76
|
+
describe "#eval_with_timeout" do
|
77
|
+
subject { Sandbox.safe }
|
78
|
+
|
79
|
+
context "before it's been activated" do
|
80
|
+
it "should protect against long running code" do
|
81
|
+
long_code = <<-RUBY
|
82
|
+
sleep(5)
|
83
|
+
RUBY
|
84
|
+
|
85
|
+
expect {
|
86
|
+
subject.eval_with_timeout(long_code, 1)
|
87
|
+
}.to raise_error(Sandbox::SandboxException, /Timeout/)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context "after it's been activated" do
|
92
|
+
before(:each) { subject.activate! }
|
93
|
+
|
94
|
+
it "should protect against long running code" do
|
95
|
+
long_code = <<-RUBY
|
96
|
+
while true; end
|
97
|
+
RUBY
|
98
|
+
|
99
|
+
expect {
|
100
|
+
Timeout.timeout(3) do
|
101
|
+
subject.eval_with_timeout(long_code, 1)
|
102
|
+
end
|
103
|
+
}.to raise_error(Sandbox::SandboxException, /Timeout/)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
85
107
|
|
86
108
|
describe "#eval" do
|
87
109
|
subject { Sandbox.new }
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jruby_sandbox
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
prerelease:
|
6
6
|
platform: java
|
7
7
|
authors:
|
@@ -10,11 +10,11 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2011-09-
|
13
|
+
date: 2011-09-22 00:00:00.000000000Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: fakefs
|
17
|
-
requirement: &
|
17
|
+
requirement: &70213141993840 !ruby/object:Gem::Requirement
|
18
18
|
none: false
|
19
19
|
requirements:
|
20
20
|
- - ! '>='
|
@@ -22,10 +22,10 @@ dependencies:
|
|
22
22
|
version: '0'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
|
-
version_requirements: *
|
25
|
+
version_requirements: *70213141993840
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
27
|
name: rake
|
28
|
-
requirement: &
|
28
|
+
requirement: &70213141992860 !ruby/object:Gem::Requirement
|
29
29
|
none: false
|
30
30
|
requirements:
|
31
31
|
- - ! '>='
|
@@ -33,10 +33,10 @@ dependencies:
|
|
33
33
|
version: '0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
|
-
version_requirements: *
|
36
|
+
version_requirements: *70213141992860
|
37
37
|
- !ruby/object:Gem::Dependency
|
38
38
|
name: rake-compiler
|
39
|
-
requirement: &
|
39
|
+
requirement: &70213141991920 !ruby/object:Gem::Requirement
|
40
40
|
none: false
|
41
41
|
requirements:
|
42
42
|
- - ! '>='
|
@@ -44,10 +44,10 @@ dependencies:
|
|
44
44
|
version: '0'
|
45
45
|
type: :development
|
46
46
|
prerelease: false
|
47
|
-
version_requirements: *
|
47
|
+
version_requirements: *70213141991920
|
48
48
|
- !ruby/object:Gem::Dependency
|
49
49
|
name: rspec
|
50
|
-
requirement: &
|
50
|
+
requirement: &70213141991120 !ruby/object:Gem::Requirement
|
51
51
|
none: false
|
52
52
|
requirements:
|
53
53
|
- - ! '>='
|
@@ -55,10 +55,10 @@ dependencies:
|
|
55
55
|
version: '0'
|
56
56
|
type: :development
|
57
57
|
prerelease: false
|
58
|
-
version_requirements: *
|
58
|
+
version_requirements: *70213141991120
|
59
59
|
- !ruby/object:Gem::Dependency
|
60
60
|
name: yard
|
61
|
-
requirement: &
|
61
|
+
requirement: &70213141989820 !ruby/object:Gem::Requirement
|
62
62
|
none: false
|
63
63
|
requirements:
|
64
64
|
- - ! '>='
|
@@ -66,7 +66,7 @@ dependencies:
|
|
66
66
|
version: '0'
|
67
67
|
type: :development
|
68
68
|
prerelease: false
|
69
|
-
version_requirements: *
|
69
|
+
version_requirements: *70213141989820
|
70
70
|
description: A version of _why's Freaky Freaky Sandbox for JRuby.
|
71
71
|
email:
|
72
72
|
- dray@envylabs.com
|
@@ -90,6 +90,7 @@ files:
|
|
90
90
|
- lib/sandbox.rb
|
91
91
|
- lib/sandbox/prelude.rb
|
92
92
|
- lib/sandbox/version.rb
|
93
|
+
- spec/exploits_spec.rb
|
93
94
|
- spec/sandbox_spec.rb
|
94
95
|
- spec/support/foo.txt
|
95
96
|
- lib/sandbox/sandbox.jar
|
@@ -118,5 +119,6 @@ signing_key:
|
|
118
119
|
specification_version: 3
|
119
120
|
summary: Sandbox support for JRuby
|
120
121
|
test_files:
|
122
|
+
- spec/exploits_spec.rb
|
121
123
|
- spec/sandbox_spec.rb
|
122
124
|
- spec/support/foo.txt
|