jruby-safe 0.2.2-java

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,3 @@
1
+ module Sandbox
2
+ VERSION = "0.2.2"
3
+ end
@@ -0,0 +1,185 @@
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 access to the filesystem using backticks" do
16
+ expect {
17
+ subject.eval(%|`cat spec/support/foo.txt`|)
18
+ }.to raise_error(Sandbox::SandboxException)
19
+ end
20
+
21
+ it "should not allow running system commands using system" do
22
+ expect {
23
+ subject.eval(%|system("ls")|)
24
+ }.to raise_error(Sandbox::SandboxException, /NoMethodError/)
25
+ end
26
+
27
+ it "should not allow running system commands through File.class_eval" do
28
+ expect {
29
+ subject.eval(%|File.class_eval { `echo Hello` }|)
30
+ }.to raise_error(Sandbox::SandboxException, /NoMethodError/)
31
+
32
+ expect {
33
+ subject.eval(%|FileUtils.class_eval { `echo Hello` }|)
34
+ }.to raise_error(Sandbox::SandboxException, /NoMethodError/)
35
+
36
+ expect {
37
+ subject.eval(%|Dir.class_eval { `echo Hello` }|)
38
+ }.to raise_error(Sandbox::SandboxException, /NoMethodError/)
39
+
40
+ expect {
41
+ subject.eval(%|FileTest.class_eval { `echo Hello` }|)
42
+ }.to raise_error(Sandbox::SandboxException, /NoMethodError/)
43
+ end
44
+
45
+ it "should not allow running system commands through File.eval" do
46
+ expect {
47
+ subject.eval(%|File.eval "`echo Hello`"|)
48
+ }.to raise_error(Sandbox::SandboxException, /NoMethodError/)
49
+
50
+ expect {
51
+ subject.eval(%|FileUtils.eval "`echo Hello`"|)
52
+ }.to raise_error(Sandbox::SandboxException, /NoMethodError/)
53
+
54
+ expect {
55
+ subject.eval(%|Dir.eval "`echo Hello`"|)
56
+ }.to raise_error(Sandbox::SandboxException, /NoMethodError/)
57
+
58
+ expect {
59
+ subject.eval(%|FileTest.eval "`echo Hello`"|)
60
+ }.to raise_error(Sandbox::SandboxException, /NoMethodError/)
61
+ end
62
+
63
+ it "should not allow running system commands through File.instance_eval" do
64
+ expect {
65
+ subject.eval(%|File.instance_eval { `echo Hello` }|)
66
+ }.to raise_error(Sandbox::SandboxException, /NoMethodError/)
67
+
68
+ expect {
69
+ subject.eval(%|FileUtils.instance_eval { `echo Hello` }|)
70
+ }.to raise_error(Sandbox::SandboxException, /NoMethodError/)
71
+
72
+ expect {
73
+ subject.eval(%|Dir.instance_eval { `echo Hello` }|)
74
+ }.to raise_error(Sandbox::SandboxException, /NoMethodError/)
75
+
76
+ expect {
77
+ subject.eval(%|FileTest.instance_eval { `echo Hello` }|)
78
+ }.to raise_error(Sandbox::SandboxException, /NoMethodError/)
79
+ end
80
+
81
+ it "should not allow running any commands or reading files using IO" do
82
+ expect {
83
+ subject.eval(%|f=IO.popen("uname"); f.readlines; f.close|)
84
+ }.to raise_error(Sandbox::SandboxException, /NoMethodError/)
85
+
86
+ expect {
87
+ subject.eval(%|IO.binread("/etc/passwd")|)
88
+ }.to raise_error(Sandbox::SandboxException, /NoMethodError/)
89
+
90
+ expect {
91
+ subject.eval(%|IO.read("/etc/passwd")|)
92
+ }.to raise_error(Sandbox::SandboxException, /NoMethodError/)
93
+ end
94
+
95
+ it "should not pass through methods added to Kernel" do
96
+ k = subject.eval(%|Kernel|)
97
+ def k.crack
98
+ open("/etc/passwd")
99
+ end
100
+
101
+ Kernel.should respond_to(:crack)
102
+ subject.eval(%|Kernel.respond_to?(:crack)|).should == false
103
+ end
104
+
105
+ it "should not allow calling fork on Kernel, even through eval" do
106
+ subject.eval(%|eval("Kernel").respond_to?(:fork)|).should == false
107
+ end
108
+
109
+ it "should not get access to outside the box objects by using eval and TOPLEVEL_BINDING" do
110
+ expect {
111
+ subject.eval(%{eval("OutsideFoo.bar", TOPLEVEL_BINDING)})
112
+ }.to raise_error(Sandbox::SandboxException, /NameError/)
113
+ end
114
+
115
+ it "should not get access to the outside eval through a ref'd object" do
116
+ subject.ref(OutsideFoo)
117
+ subject.eval(%|obj = OutsideFoo.new|)
118
+ subject.eval(%|(obj.methods.grep /^eval/).empty?|).should == true
119
+ subject.eval(%|obj.respond_to?(:eval)|).should == false
120
+ end
121
+
122
+ it "should not allow file access, even through a ref hack" do
123
+ expect {
124
+ subject.eval(%|File.open("/etc/passwd").read|)
125
+ }.to raise_error(Sandbox::SandboxException)
126
+
127
+ subject.ref(OutsideFoo)
128
+ subject.eval(%|obj = OutsideFoo.new|)
129
+
130
+ pending "gotta figure out how to lock down ref'd objects eval" do
131
+ expect {
132
+ subject.eval(%|obj.eval("File.open(\\"/etc/passwd\\").read")|)
133
+ }.to raise_error(Sandbox::SandboxException)
134
+ end
135
+ end
136
+
137
+ it "should have safe globals" do
138
+ subject.eval(%|$0|).should == "(sandbox)"
139
+ /(.)(.)(.)/.match("abc")
140
+ subject.eval(%|$TEST = "TEST"; $TEST|).should == "TEST"
141
+ subject.eval(%|/(.)(.)(.)/.match("def"); $2|).should == "e"
142
+ $2.should == "b"
143
+ subject.eval(%|$TEST|).should == "TEST"
144
+ subject.eval(%|$2|).should == "e"
145
+ end
146
+
147
+ it "should not keep Kernel.fork" do
148
+ expect {
149
+ subject.eval(%|Kernel.fork|)
150
+ }.to raise_error(Sandbox::SandboxException)
151
+
152
+ expect {
153
+ subject.eval(%|fork|)
154
+ }.to raise_error(Sandbox::SandboxException)
155
+ end
156
+
157
+ it "should not allow the sandbox to get back to Kernel through ancestors" do
158
+ subject.eval(%|$0.class.ancestors[3].respond_to?(:fork)|).should == false
159
+ end
160
+
161
+ it "should not pass through block scope" do
162
+ 1.times do |i|
163
+ subject.eval(%|local_variables|).should == []
164
+ end
165
+ end
166
+
167
+ it "should not allow exploits through match data" do
168
+ subject.eval(%|begin; /(.+)/.match("FreakyFreaky").instance_eval { open("/etc/passwd") }; rescue NameError; :NameError; end|).should == :NameError
169
+
170
+ subject.eval(%|begin;(begin;Regexp.new("(");rescue e;e;end).instance_eval{ open("/etc/passwd") }; rescue NameError; :NameError; end|).should == :NameError
171
+ end
172
+
173
+ it "should not be able to access outside box Kernel through exceptions" do
174
+ exception_code = <<-RUBY
175
+ begin
176
+ raise
177
+ rescue => e
178
+ obj = e
179
+ end
180
+ RUBY
181
+ subject.eval(exception_code)
182
+
183
+ subject.eval(%|obj.class.ancestors[4].respond_to?(:fork)|).should == false
184
+ end
185
+ end
@@ -0,0 +1,319 @@
1
+ require "rspec"
2
+ require "sandbox"
3
+ require "timeout"
4
+
5
+ describe Sandbox do
6
+ after(:each) do
7
+ Object.class_eval { remove_const(:Foo) } if defined?(Foo)
8
+ end
9
+
10
+ describe ".new" do
11
+ subject { Sandbox.new }
12
+
13
+ it { should_not be_nil }
14
+ it { should be_an_instance_of(Sandbox::Full) }
15
+ end
16
+
17
+ describe ".safe" do
18
+
19
+ subject { Sandbox.safe }
20
+
21
+ it { should be_an_instance_of(Sandbox::Safe) }
22
+
23
+ it "should not lock down until calling activate!" do
24
+ subject.eval(%|`echo hello`|).should == "hello\n"
25
+
26
+ subject.activate!
27
+
28
+ expect {
29
+ subject.eval(%|`echo hello`|)
30
+ }.to raise_error(Sandbox::SandboxException)
31
+ end
32
+
33
+ it "should activate FakeFS inside the sandbox (and not allow it to be deactivated)" do
34
+ subject.eval(%|File|).should == ::File
35
+
36
+ subject.activate!
37
+
38
+ foo = File.join(File.dirname(__FILE__), "support", "foo.txt")
39
+
40
+ expect {
41
+ subject.eval(%{File.read("#{foo}")})
42
+ }.to raise_error(Sandbox::SandboxException, /Errno::ENOENT: No such file or directory/)
43
+
44
+ subject.eval(%|File|).should == FakeFS::File
45
+ subject.eval(%|Dir|).should == FakeFS::Dir
46
+ subject.eval(%|FileUtils|).should == FakeFS::FileUtils
47
+ subject.eval(%|FileTest|).should == FakeFS::FileTest
48
+
49
+ subject.eval(%{FakeFS.deactivate!})
50
+
51
+ expect {
52
+ subject.eval(%{File.read("#{foo}")})
53
+ }.to raise_error(Sandbox::SandboxException, /Errno::ENOENT: No such file or directory/)
54
+
55
+ subject.eval(%{File.open("/bar.txt", "w") {|file| file << "bar" }})
56
+
57
+ expect {
58
+ subject.eval(%{FileUtils.cp("/bar.txt", "/baz.txt")})
59
+ }.to_not raise_error(Sandbox::SandboxException, /NoMethodError/)
60
+ end
61
+
62
+ context "eval_with_binding" do
63
+ let(:b) { binding }
64
+
65
+ it "should not lock down until calling activate!" do
66
+ subject.eval(%|`echo hello`|, b).should == "hello\n"
67
+
68
+ subject.activate!
69
+
70
+ expect {
71
+ subject.eval(%|`echo hello`|, b)
72
+ }.to raise_error(Sandbox::SandboxException)
73
+ end
74
+
75
+ it "should activate FakeFS inside the sandbox (and not allow it to be deactivated)" do
76
+ subject.eval(%|File|, b).should == ::File
77
+
78
+ subject.activate!
79
+
80
+ foo = File.join(File.dirname(__FILE__), "support", "foo.txt")
81
+
82
+ expect {
83
+ subject.eval(%{File.read("#{foo}")}, b)
84
+ }.to raise_error(Sandbox::SandboxException, /Errno::ENOENT: No such file or directory/)
85
+
86
+ subject.eval(%|File|, b).should == FakeFS::File
87
+ subject.eval(%|Dir|, b).should == FakeFS::Dir
88
+ subject.eval(%|FileUtils|, b).should == FakeFS::FileUtils
89
+ subject.eval(%|FileTest|, b).should == FakeFS::FileTest
90
+
91
+ subject.eval(%{FakeFS.deactivate!}, b)
92
+
93
+ expect {
94
+ subject.eval(%{File.read("#{foo}")}, b)
95
+ }.to raise_error(Sandbox::SandboxException, /Errno::ENOENT: No such file or directory/)
96
+
97
+ subject.eval(%{File.open("/bar.txt", "w") {|file| file << "bar" }}, b)
98
+
99
+ expect {
100
+ subject.eval(%{FileUtils.cp("/bar.txt", "/baz.txt")}, b)
101
+ }.to_not raise_error(Sandbox::SandboxException, /NoMethodError/)
102
+ end
103
+
104
+ it "can set and use local variable" do
105
+ b["x"] = 4
106
+ subject.eval("x", b).should == 4
107
+ end
108
+ end
109
+ end
110
+
111
+ describe ".current" do
112
+ it "should not return a current sandbox outside a sandbox" do
113
+ Sandbox.current.should be_nil
114
+ end
115
+
116
+ it "should return the current sandbox inside a sandbox" do
117
+ pending do
118
+ sandbox = Sandbox.new
119
+ sandbox.ref(Sandbox)
120
+ sandbox.eval(%|Sandbox.current|).should == sandbox
121
+ end
122
+ end
123
+ end
124
+
125
+ describe "#eval with timeout" do
126
+ subject { Sandbox.safe }
127
+
128
+ context "before it's been activated" do
129
+ it "should protect against long running code" do
130
+ long_code = <<-RUBY
131
+ sleep(5)
132
+ RUBY
133
+
134
+ expect {
135
+ subject.eval(long_code, timeout: 1)
136
+ }.to raise_error(Sandbox::TimeoutError)
137
+ end
138
+
139
+ it "should not raise a timeout error if the code runs in under the passed in time" do
140
+ short_code = <<-RUBY
141
+ 1+1
142
+ RUBY
143
+
144
+ expect {
145
+ subject.eval(short_code, timeout: 1)
146
+ }.to_not raise_error(Sandbox::TimeoutError)
147
+ end
148
+ end
149
+
150
+ context "after it's been activated" do
151
+ before(:each) { subject.activate! }
152
+
153
+ it "should protect against long running code" do
154
+ long_code = <<-RUBY
155
+ while true; end
156
+ RUBY
157
+
158
+ expect {
159
+ subject.eval(long_code, timeout: 1)
160
+ }.to raise_error(Sandbox::TimeoutError)
161
+ end
162
+
163
+ it "should persist state between evaluations" do
164
+ subject.eval(%|o = Object.new|, timeout: 1)
165
+
166
+ expect {
167
+ subject.eval(%|o|, timeout: 1)
168
+ }.to_not raise_error(Sandbox::SandboxException)
169
+ end
170
+ end
171
+ end
172
+
173
+ describe "#eval" do
174
+ subject { Sandbox.new }
175
+
176
+ it "should allow a range of common operations" do
177
+ operations = <<-OPS
178
+ 1 + 1
179
+ "foo".chomp
180
+ "foo"
181
+ OPS
182
+ subject.eval(operations).should == "foo"
183
+ end
184
+
185
+ it "should have an empty ENV" do
186
+ pending do
187
+ subject.eval(%{ENV.to_a}).should be_empty
188
+ end
189
+ end
190
+
191
+ it "should persist state between evaluations" do
192
+ subject.eval(%|o = Object.new|)
193
+ subject.eval(%|o|).should_not be_nil
194
+ end
195
+
196
+ it "should be able to define a new class in the sandbox" do
197
+ result = subject.eval(%|Foo = Struct.new(:foo); struct = Foo.new("baz"); struct.foo|)
198
+ result.should == "baz"
199
+ end
200
+
201
+ it "should be able to use a class across invocations" do
202
+ # Return nil, because the environment doesn't know "Foo"
203
+ subject.eval(%|Foo = Struct.new(:foo); nil|)
204
+ subject.eval(%|struct = Foo.new("baz"); nil|)
205
+ subject.eval(%|struct.foo|).should == "baz"
206
+ end
207
+
208
+ describe "communication between sandbox and environment" do
209
+ it "should be possible to pass data from the box to the environment" do
210
+ Foo = Struct.new(:foo)
211
+ subject.ref(Foo)
212
+ struct = subject.eval(%|struct = Foo.new|)
213
+ subject.eval(%|struct.foo = "baz"|)
214
+ struct.foo.should == "baz"
215
+ end
216
+
217
+ it "should be possible to pass data from the environment to the box" do
218
+ Foo = Struct.new(:foo)
219
+ subject.ref(Foo)
220
+ struct = subject.eval(%|struct = Foo.new|)
221
+ struct.foo = "baz"
222
+ subject.eval(%|struct.foo|).should == "baz"
223
+ end
224
+
225
+ it "should be able to pass large object data from the box to the environment" do
226
+ expect {
227
+ subject.eval %{
228
+ (0..1000).to_a.inject({}) {|h,i| h[i] = "HELLO WORLD"; h }
229
+ }
230
+ }.to_not raise_error(Sandbox::SandboxException)
231
+
232
+ expect {
233
+ subject.eval %{"RUBY"*100}
234
+ }.to_not raise_error(Sandbox::SandboxException)
235
+ end
236
+ end
237
+ end
238
+
239
+ describe "#import" do
240
+ subject { Sandbox.new }
241
+
242
+ it "should be able to call a referenced namespaced module method" do
243
+ Foo = Class.new
244
+ Foo::Bar = Module.new do
245
+ def baz
246
+ "baz"
247
+ end
248
+ module_function :baz
249
+ end
250
+
251
+ subject.import(Foo::Bar)
252
+ subject.eval(%|Foo::Bar.baz|).should == "baz"
253
+ end
254
+
255
+ it "should be able to include a module from the environment" do
256
+ Foo = Module.new do
257
+ def baz
258
+ "baz"
259
+ end
260
+ end
261
+
262
+ subject.import(Foo)
263
+ subject.eval(%|class Bar; include Foo; end; nil|)
264
+ subject.eval(%|Bar.new.baz|).should == "baz"
265
+ end
266
+
267
+ it "should be able to copy instance methods from a module that uses module_function" do
268
+ Foo = Module.new do
269
+ def baz; "baz"; end
270
+
271
+ module_function :baz
272
+ end
273
+
274
+ subject.import Foo
275
+ subject.eval(%|Foo.baz|).should == "baz"
276
+ end
277
+ end
278
+
279
+ describe "#ref" do
280
+ subject { Sandbox.new }
281
+
282
+ it "should be possible to reference a class defined outside the box" do
283
+ Foo = Class.new
284
+ subject.ref(Foo)
285
+ subject.eval(%|Foo.new|).should be_an_instance_of(Foo)
286
+ end
287
+
288
+ it "should be possible to change the class after the ref" do
289
+ Foo = Class.new
290
+ subject.ref(Foo)
291
+ def Foo.foo; "baz"; end
292
+ subject.eval(%|Foo.foo|).should == "baz"
293
+ end
294
+
295
+ it "should be possible to dynamically add a class method after the ref" do
296
+ Foo = Class.new
297
+ subject.ref(Foo)
298
+ Foo.class_eval(%|def Foo.foo; "baz"; end|)
299
+ subject.eval(%|Foo.foo|).should == "baz"
300
+ end
301
+
302
+ it "should be possible to dynamically add a class method after the ref" do
303
+ Foo = Class.new
304
+ subject.ref(Foo)
305
+ Foo.instance_eval(%|def Foo.foo; "baz"; end|)
306
+ subject.eval(%|Foo.foo|).should == "baz"
307
+ end
308
+
309
+ it "should be possible to call a method on the class that receives a block" do
310
+ Foo = Class.new do
311
+ def self.bar
312
+ yield
313
+ end
314
+ end
315
+ subject.ref(Foo)
316
+ subject.eval(%|Foo.bar { "baz" }|).should == "baz"
317
+ end
318
+ end
319
+ end