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 CHANGED
@@ -7,7 +7,7 @@ GIT
7
7
  PATH
8
8
  remote: .
9
9
  specs:
10
- jruby_sandbox (0.1.0-java)
10
+ jruby_sandbox (0.1.1-java)
11
11
  fakefs
12
12
 
13
13
  GEM
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
 
@@ -1,3 +1,3 @@
1
1
  module Sandbox
2
- VERSION = '0.1.1'
2
+ VERSION = '0.1.2'
3
3
  end
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.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-20 00:00:00.000000000Z
13
+ date: 2011-09-22 00:00:00.000000000Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: fakefs
17
- requirement: &70184226086860 !ruby/object:Gem::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: *70184226086860
25
+ version_requirements: *70213141993840
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: rake
28
- requirement: &70184226086440 !ruby/object:Gem::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: *70184226086440
36
+ version_requirements: *70213141992860
37
37
  - !ruby/object:Gem::Dependency
38
38
  name: rake-compiler
39
- requirement: &70184226086020 !ruby/object:Gem::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: *70184226086020
47
+ version_requirements: *70213141991920
48
48
  - !ruby/object:Gem::Dependency
49
49
  name: rspec
50
- requirement: &70184226085600 !ruby/object:Gem::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: *70184226085600
58
+ version_requirements: *70213141991120
59
59
  - !ruby/object:Gem::Dependency
60
60
  name: yard
61
- requirement: &70184226085180 !ruby/object:Gem::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: *70184226085180
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